From 18e9193fc190cb40ed1c2ebcac8b0a4859b79090 Mon Sep 17 00:00:00 2001 From: Jay Date: Wed, 30 Aug 2023 12:52:31 -0400 Subject: [PATCH] TS update and archiving old chapters (#730) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Guide with Typescript & PNPM (#719) As I work through the guide, I add notes and I also read the comments and adding any relevant notes I see as of May 18(ish). ~~ Site Wide **** Changes - Added .idea to .gitignore - Added .idea to Jekyll Exclude list **** Notes - [ ] Consider accessibility features * http://www.goring.org/resources/accessibility.html * https://github.com/benbalter/ra11y * adding an indicator for links that open in new tabs - [ ] Consider adding "Archived" banner to the top of any parts of the site that are kept but are not intended for current reference for example, all the pages [in this section](http://localhost:4000/guide.html#archives). ~~ Guide **** Changes - Updated External Links to open a new tab. - Updated SASS for blockquote within post-content to not be enormous. - Added a few classes for Notes and Asides **** Notes - [ ] TODO: Resolve SST Console Warning [See the migration guide](https://a.co/7PzMCcy) (node:85684) NOTE: We are formalizing our plans to enter AWS SDK for JavaScript (v2) into maintenance mode in 2023. Please migrate your code to use AWS SDK for JavaScript (v3). For more information, check the migration guide at https://a.co/7PzMCcy (Use `node --trace-warnings ...` to show where the warning was created) 16:14:43.391 - [ ] TODO: Clean up legacy guide files so it doesn't clutter the current fileset. - [ ] TODO: Correctly manage the path_id variable in code examples to ensure they are present without code duplication. - [ ] TODO: Switch from Amplify API to RestApi or GraphQLAPI based on note in API.d.ts (See Warning in console.sst.dev) - [ ] Consider adding a chapter on accessibility in the frontend creation section. * [React Accessibility](https://legacy.reactjs.org/docs/accessibility.html) - New Accessibility guides are still pending based on announcement on 3/16/2023 * https://www.freecodecamp.org/news/react-accessibility-tools-build-accessible-react-apps/ * https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility ~~ Chapter Specific @@@ What Does This Guide Cover **** Notes (From Discourse comments) - [ ] [Consider using Next.js for the guide](https://discourse.sst.dev/t/what-does-this-guide-cover/83/7?u=christinep2) - [ ] [Consider switching example to React Native](https://discourse.sst.dev/t/what-does-this-guide-cover/83/8?u=christinep2) @@@ How to Get Help? _no specific changes_ @@@ What is Serverless? **** Changes - Removed link to the What is AWS Lambda chapter as it encourages skipping the rest of the current page. @@@ What is AWS Lambda? **** Notes - [ ] [Consider decreasing specificity in Lambda Spec Description](/chapters/what-is-aws-lambda.html#lambda-specs) - As AWS modifies its offerings, the description can be outdated. By speaking in generalities and linking directly to AWS for specificity the document will be less fragile. @@@ Why Create Serverless Apps? **** Notes - [ ] Consider specifying a timeframe on the cost calculation to account for pricing changes over time. @@@ Create an AWS Account **** Changes - Tested steps with a new AWS Account. - Removed reference to specific language since they changed the call to action. Also removed screenshot as it was outdated. @@@ Create an IAM User **** Changes - Updated chapter instructions & screenshots. - Updated screenshots at 1280x783. - Screenshots located in `create-iam-user` - Left old screenshots in `iam-user` for other languages as I couldn't update them to match the new instructions. @@@ Configure the AWS CLI **** Changes - Tested steps with a clean install on Mac. **** Notes - [ ] Consider adding [Chocolatey](https://chocolatey.org/) [instructions for Windows](https://discourse.sst.dev/t/configure-the-aws-cli/86/3?u=christinep2. - [ ] (Consider adding instructions for *NIX flavors.)[https://discourse.sst.dev/t/configure-the-aws-cli/86/5?u=christinep2] - [ ] Consider adding a link to the AWS instructions instead, in case that is easier. @@@ What is SST? **** Changes - Removed link to other chapters as it encourages skipping the current document - Added links for the specific IDE Instructions **** Notes - [ ] Consider removing the penultimate sentence regarding the alternative guide since it is archived. @@@ What is Infrastructure as Code? **** Changes - Added "using CDK" into the first sentence based on the confusion mentioned in the chapter comments @@@ What is AWS CDK? **** Changes - Changed references to pnpm - Added question mark to title to match the other titles in the section and the title of the comments page. @@@ Create an SST app **** Changes - updated to pnpm **** Notes - [ ] Need to update Next Steps command line output as follows - cd notes - pnpm add (or npm, or yarn) - pnpm exec sst dev @@@ Create a Hello World API **** Changes - updated to pnpm - Added instructions for Safari et. al. users found in [this discord thread](https://discord.com/channels/983865673656705025/1102040862143303751/1102073623516282890) **** Notes - [ ] Safari instructions for mkcert usage are not working. Need to investigate further. @@@ Initialize a Github Repo _no specific changes_ @@@ Create a DynamoDB Table in SST **** Changes - updated to pnpm - updated to typescript - Added clarification as specified in comment **** Questions 1. Regarding the statement "There’s no specific reason why we are creating a separate stack for these resources." Would it be fair to say that the decision to have separate stacks increases application maintainability? It seems like it could fall under the principle of "separation of concerns". 2. Why is the Hello World API still present in console after this step? @@@ Create a S3 Bucket in SST **** Changes - updated to pnpm - updated to typescript - small wording changes - moved bucket in return to above table instead of below, matching location in the stack file and alphabetically ordered. - git commit as a one line command to facilitate using the copy option for the code. @@@ Review Our App Architecture _no specific changes_ @@@ Add an API to Create a Note **** Changes - updated to pnpm - updated to typescript - Added info about DRYness and maintainability to the refactor section. **** Notes - Should we uninstall the aws-sdk (or @aws-sdk) from functions when we install it into core? - Should the user re-test after the refactor to ensure it still works? - Do we need to add [this note](https://discourse.sst.dev/t/add-an-api-to-create-a-note/2451/18?u=christinep2) for Windows users into the guide? - Referenced [this error handling blog post](https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript) for the error code in handler. - I'm seeing a delay as I add the API endpoints with seeing them in the console. I experienced on two different run-throughs, is this expected? If so, can we add a note to that effect at this point so people don't try and debug? @@@ Add an API to Get/Put/Patch/Delete a Note **** Changes - updated to pnpm - updated to typescript @@@ Adding Auth to Our Serverless App **** Changes - Updated to typescript - Updated to use docs variable instead of hardcoded url @@@ Secure Our Serverless APIs **** Changes - Updated to typescript - Switched to PNPM - Used optional chaining operator for `requestContext.authorizer`. Should we have a different solution? @@@ Setup a Stripe Account **** Changes - Redo screenshots and update text to match new UI @@@ Handling Secrets in SST **** Changes - Rewrite to use the SST Secrets CLI based on the information found here: https://docs.sst.dev/config#should-i-use-configsecret-or-env-for-secrets @@@ Add an API to Handle Billing **** Changes - Updated to typescript - Switched to PNPM **** Notes - [ ] Consider not using [nested ternary operators](https://medium.com/@benlmsc/stop-using-nested-ternary-operators-heres-why-53e7e078e65a). @@@ Unit Tests in Serverless **** Changes - Switch to Typescript for testing - Switch to PNPM - Add instructions to add Vite Test to workspace **** Notes - [ ] Consider including tests as we build out the API instead of at the end, or at least adding a note that this is a preferred approach for ensuring properly tested code. @@@ Handle CORS in Serverless APIs **** Changes - Switch to Typescript - Switch to PNPM @@@ Handle CORS in S3 for File Uploads **** Changes - Switch to Typescript - Switch to PNPM @@@ Create a New React.js App **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes **** Notes - [ ] Need to [replace create-react-app with one of the recommended solutions](https://github.com/facebook/create-react-app/issues/13072) as create-react-app is being deprecated. - [ ] Consider adding tests alongside new code from this point forward. @@@ Set up Custom Fonts **** Changes - Minor wording changes - Update final screenshot to show tsx file instead of js file @@@ Set up Bootstrap **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes @@@ Handle Routes with React Router **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes @@@ Create Containers **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes @@@ Adding Links in the Navbar **** Changes - Switch to PNPM - Switch to Typescript - Minor wording changes @@@ Handle 404s **** Changes - Switch to Typescript - Minor wording changes @@@ Configure AWS Amplify **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes @@@ Create a Login Page **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes @@@ Login with AWS Cognito **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes @@@ Add the Session to the State **** Changes - Switch to Typescript @@@ Load the State from the Session **** Changes - Switch to Typescript - Move import of UseEffect above explainer so that consumer is not sidetracked by the import not being present. @@@ Clear the Session on Logout **** Changes - Switch to Typescript @@@ Redirect on Login and Logout **** Changes - Switch to Typescript @@@ Give Feedback While Logging In **** Changes - Switch to Typescript @@@ Create a Custom React Hook to Handle Form Fields **** Changes - Switch to Typescript @@@ Create a Signup Page No Changes @@@ Create the Signup Form **** Changes - Switch to Typescript - Minor wording changes @@@ Signup with AWS Cognito **** Changes - Switch to Typescript - Minor wording changes @@@ Notes - [ ] Do we want to add the handling for UserExists in? https://discourse.sst.dev/t/signup-with-aws-cognito/130/74?u=christinep2 @@@ Add the Create Note Page **** Changes - Switch to Typescript - Minor wording changes @@@ Call the Create API **** Changes - Switch to Typescript - Minor wording changes @@@ Upload a File to S3 **** Changes - Switch to Typescript @@@ Upload a File to S3 **** Changes - Switch to Typescript - Added helpful note from @sometimescasey **** Notes - [ ] Update troubleshooting tips if outdated. @@@ List All the Notes **** Changes - Switch to Typescript - Minor wording changes @@@ Call the List API **** Changes - Switch to Typescript - Move Import immediately after adding code so consumer is not distracted by errors @@@ Display a Note **** Changes - Switch to Typescript @@@ Render the Note Form **** Changes - Switch to Typescript - Minor wording changes - Move Import immediately after adding code so consumer is not distracted by errors @@@ Save Changes to a Note **** Changes - Switch to Typescript - Minor wording changes @@@ Delete a Note **** Changes - Switch to Typescript - Minor wording changes @@@ Create a Settings Page **** Changes - Switch to Typescript - Minor wording changes @@@ Add Stripe Keys to Config **** Changes - Switch to Typescript - Switch to PNPM - Minor wording changes @@@ Create a Billing Form **** Changes - Switch to Typescript - Switch to PNPM @@@ Connect the Billing Form **** Changes - Switch to Typescript - Switch to PNPM @@@ Set up Secure Pages No Changes @@@ Create a Route That Redirects **** Changes - Switch to Typescript @@@ Use the Redirect Routes **** Changes - Switch to Typescript @@@ Redirect on Login **** Changes - Switch to Typescript @@@ Purchase a Domain with Route 53 Skipped. @@@ Custom Domains in serverless APIs **** Changes - Switch to Typescript - Minor wording changes @@@ Custom Domains for React Apps on AWS **** Changes - Switch to Typescript/PNPM - Minor wording changes **** Notes - [ ] I tried switching the domain and the alias so that I could easily remove the domain alias and use it for my "real" app and leave the subdomain for this demo, but it erred with the message. ``` Error: Validation failed with the following errors: [prod-notes-FrontendStack/ReactSite/Redirect/RedirectCertificate] DNS zone www-notes.manuals4life.com is not authoritative for certificate domain name manuals4life.com ``` In orde to keep the statement "You can switch these around so that the root domain redirects to the `www.` version as well.", I believe we'd need additional information dealing with the CAA record or whatever tripped it up. @@@ Getting Production Ready No Changes @@@ Creating a CI/CD Pipeline for serverless No Changes (other than the global new tab for external links) @@@ Setting up Your Project on Seed **** Changes - Switch References to Typescript - Minor wording changes **** Notes - [ ] Needs new Screenshots using serverless-stack demo notes app. - [ ] Might add a screenshot pointing at settings in the UI? @@@ Configure Secrets in Seed **** Changes - Switch References to sst secret instead of .env.local - Minor wording changes @@@ Deploying Through Seed **** Changes - minor wording changes @@@ Debugging Full-Stack Serverless Apps No Changes @@@ Setup Error Reporting in React **** Changes - Switch to Typescript - Switched to `pnpm add @sentry/react` from `@sentry/browser` - Added an ErrorInfoType **** Notes - [ ] Needs new Screenshots @@@ Report API Errors in React **** Changes - Switch to Typescript @@@ Setup an Error Boundary in React **** Changes - Switch to Typescript - Change multiline ternary to if/else - Updated screenshot for react error **** Notes - [ ] Needs new Screenshots for Sentry confirmations @@@ Setup Error Logging in Serverless **** Changes - Switch to Typescript @@@ Logic Errors in Lambda Functions **** Changes - Switch to Typescript **** Notes - [ ] Logging extra information is incorrectly displaying `[object Object]` @@@ Unexpected Errors in Lambda Functions **** Changes - Switch references to Typescript @@@ Errors Outside Lambda Functions **** Changes - Switch references to Typescript @@@ Errors in API Gateway **** Changes - Switch references to Typescript @@@ Wrapping Up **** Changes - Switch references to PNPM ~~ Additional Guide Information Pages still in use: @@@ Auth In Serverless Apps **** Changes - Updated to typescript - Updated referenced pages What is IAM and What is an ARN @@@ What is IAM / What is an ARN **** Changes - Updated text to remove wording implying it is a step in the guide @@@ Mapping Cognito Identity Id and User Pool Id @@@ Cognito User Pool vs Identity Pool @@@ Setting serverless environment variables in a React app - Updated to pnpm - Updated to typescript ~~ Outstanding Changes @@@ Extra Credit series of chapters on user management - [ ] TODO: Update these as they are referenced from "Give Feedback While Logging In" (Specifically links to http://localhost:4000/chapters/manage-user-accounts-in-aws-amplify.html) Co-authored-by: Jay * Edits * Edits * Reviewing changes * Removing test code * Edits * Setting up sst * Moving to archives * Updating links to archives * Adding redirects * Adding redirects --------- Co-authored-by: Christine Panus --- .gitignore | 3 + Gemfile.lock | 5 +- README.md | 3 +- {_chapters => _archives}/add-a-billing-api.md | 1 + .../add-a-create-note-api.md | 7 +- .../add-a-delete-note-api.md | 1 + .../add-a-get-note-api.md | 1 + .../add-a-list-all-the-notes-api.md | 1 + .../add-an-update-note-api.md | 1 + .../add-support-for-es6-and-typescript.md | 9 +- .../allow-users-to-change-passwords.md | 1 + .../allow-users-to-change-their-email.md | 1 + .../api-gateway-and-lambda-logs.md | 1 + .../backups-in-dynamodb.md | 1 + ...-practices-for-building-serverless-apps.md | 35 +- .../code-splitting-in-create-react-app.md | 1 + .../cognito-user-pool-vs-identity-pool.md | 1 + ...ure-cognito-identity-pool-in-serverless.md | 10 +- ...nfigure-cognito-user-pool-in-serverless.md | 3 +- .../configure-dynamodb-in-serverless.md | 6 +- .../configure-multiple-aws-profiles.md | 3 +- .../configure-s3-in-serverless.md | 6 +- .../connect-to-api-gateway-with-iam-auth.md | 1 + .../create-a-cloudfront-distribution.md | 1 + .../create-a-cognito-identity-pool.md | 7 +- .../create-a-cognito-test-user.md | 1 + .../create-a-cognito-user-pool.md | 1 + .../create-a-dynamodb-table.md | 3 +- .../create-a-netlify-build-script.md | 8 +- .../create-an-s3-bucket-for-file-uploads.md | 6 +- .../create-an-s3-bucket.md | 6 +- .../creating-a-ci-cd-pipeline-for-react.md | 6 +- .../creating-feature-environments.md | 3 +- .../creating-pull-request-environments.md | 1 + .../cross-stack-references-in-serverless.md | 3 +- .../custom-domain-in-netlify.md | 5 +- .../customize-the-serverless-iam-policy.md | 1 + .../debugging-serverless-api-issues.md | 7 +- ...ploy-a-serverless-app-with-dependencies.md | 4 +- .../deploy-the-api-services-repo.md | 1 + {_chapters => _archives}/deploy-the-apis.md | 3 +- .../deploy-the-resources-repo.md | 3 +- {_chapters => _archives}/deploy-to-s3.md | 3 +- {_chapters => _archives}/deploy-updates.md | 6 +- .../deploy-your-hello-world-api.md | 3 +- .../deploy-your-serverless-infrastructure.md | 5 +- .../deploying-a-react-app-to-aws.md | 4 +- .../deploying-a-react-app-to-netlify.md | 6 +- .../deploying-only-updated-services.md | 1 + .../deploying-to-multiple-aws-accounts.md | 1 + ...ate-social-share-images-with-serverless.md | 1 + .../environments-in-create-react-app.md | 3 +- .../environments-in-serverless-apps.md | 1 + ...ok-login-with-cognito-using-aws-amplify.md | 3 +- .../frontend-workflow-in-netlify.md | 7 +- .../handle-api-gateway-cors-errors.md | 1 + .../handle-forgot-and-reset-password.md | 1 + ...-add-authentication-to-a-serverless-app.md | 13 +- .../invoke-api-gateway-endpoints-locally.md | 1 + .../invoke-lambda-functions-locally.md | 1 + .../load-secrets-from-env.md | 4 +- ...ge-aws-accounts-using-aws-organizations.md | 1 + .../manage-environment-related-config.md | 6 +- ...manage-environments-in-create-react-app.md | 1 + .../manage-user-accounts-in-aws-amplify.md | 1 + ...ng-cognito-identity-id-and-user-pool-id.md | 3 +- .../monitor-usage-for-environments.md | 1 + .../organizing-serverless-projects.md | 7 +- .../package-lambdas-with-serverless-bundle.md | 1 + ...parameterize-serverless-resources-names.md | 1 + .../promoting-to-production.md | 1 + {_chapters => _archives}/rollback-changes.md | 3 +- {_chapters => _archives}/secure-the-apis.md | 7 +- .../serverless-environment-variables.md | 1 + .../serverless-nodejs-starter.md | 4 +- .../set-custom-domains-through-seed.md | 1 + ...s-environments-variables-in-a-react-app.md | 21 +- .../setting-up-your-project-on-netlify.md | 1 + .../setup-a-custom-domain-with-ssl.md | 4 +- .../setup-the-serverless-framework.md | 1 + .../setup-www-domain-redirect.md | 1 + .../setup-your-domain-with-cloudfront.md | 1 + .../share-an-api-endpoint-between-services.md | 6 +- .../share-code-between-services.md | 3 +- ...re-route-53-domains-across-aws-accounts.md | 3 +- .../stages-in-serverless-framework.md | 5 +- .../storing-secrets-in-serverless-apps.md | 3 +- ...ucture-environments-across-aws-accounts.md | 3 +- {_chapters => _archives}/test-the-apis.md | 15 +- .../test-the-billing-api.md | 1 + .../tracing-serverless-apps-with-x-ray.md | 1 + .../understanding-react-hooks.md | 1 + ...using-aws-cdk-with-serverless-framework.md | 8 +- ...-add-authentication-to-a-serverless-app.md | 7 +- ...rna-and-yarn-workspaces-with-serverless.md | 3 +- {_chapters => _archives}/what-is-an-arn.md | 7 +- .../what-is-aws-appsync.md | 3 +- {_chapters => _archives}/what-is-iam.md | 9 +- .../working-on-serverless-apps.md | 13 +- .../working-with-3rd-party-apis.md | 1 + .../wrapping-up-the-best-practices.md | 1 + _chapters/add-an-api-to-create-a-note.md | 221 +- _chapters/add-an-api-to-delete-a-note.md | 37 +- _chapters/add-an-api-to-get-a-note.md | 43 +- _chapters/add-an-api-to-handle-billing.md | 66 +- _chapters/add-an-api-to-list-all-the-notes.md | 40 +- _chapters/add-an-api-to-update-a-note.md | 48 +- _chapters/add-app-favicons.md | 62 +- _chapters/add-stripe-keys-to-config.md | 22 +- _chapters/add-the-create-note-page.md | 41 +- _chapters/add-the-session-to-the-state.md | 93 +- .../adding-auth-to-our-serverless-app.md | 90 +- _chapters/adding-links-in-the-navbar.md | 41 +- _chapters/auth-in-serverless-apps.md | 16 +- _chapters/call-the-create-api.md | 50 +- _chapters/call-the-list-api.md | 60 +- _chapters/clear-the-session-on-logout.md | 4 +- _chapters/configure-aws-amplify.md | 69 +- _chapters/configure-secrets-in-seed.md | 12 +- _chapters/connect-the-billing-form.md | 54 +- _chapters/create-a-billing-form.md | 120 +- ...custom-react-hook-to-handle-form-fields.md | 164 +- _chapters/create-a-dynamodb-table-in-sst.md | 32 +- _chapters/create-a-hello-world-api.md | 20 +- _chapters/create-a-login-page.md | 67 +- _chapters/create-a-new-reactjs-app.md | 119 +- _chapters/create-a-route-that-redirects.md | 24 +- _chapters/create-a-settings-page.md | 96 +- _chapters/create-a-signup-page.md | 2 +- _chapters/create-an-aws-account.md | 6 +- _chapters/create-an-iam-user.md | 64 +- _chapters/create-an-s3-bucket-in-sst.md | 39 +- _chapters/create-an-sst-app.md | 8 +- _chapters/create-containers.md | 41 +- _chapters/create-the-signup-form.md | 161 +- ...reating-a-ci-cd-pipeline-for-serverless.md | 10 +- .../custom-domains-for-react-apps-on-aws.md | 38 +- .../custom-domains-in-serverless-apis.md | 31 +- _chapters/delete-a-note.md | 10 +- _chapters/deploying-through-seed.md | 30 +- _chapters/display-a-note.md | 18 +- _chapters/errors-in-api-gateway.md | 16 +- _chapters/errors-outside-lambda-functions.md | 75 +- .../es/add-support-for-es6-and-typescript.md | 4 +- _chapters/es/create-a-dynamodb-table.md | 2 +- _chapters/es/create-an-iam-user.md | 2 +- _chapters/es/review-our-app-architecture.md | 2 +- _chapters/further-reading.md | 14 +- _chapters/getting-production-ready.md | 66 +- _chapters/give-feedback-while-logging-in.md | 87 +- _chapters/handle-404s.md | 15 +- .../handle-cors-in-s3-for-file-uploads.md | 8 +- _chapters/handle-cors-in-serverless-apis.md | 22 +- _chapters/handle-routes-with-react-router.md | 42 +- _chapters/handling-secrets-in-sst.md | 60 +- _chapters/how-to-get-help.md | 9 +- _chapters/initialize-a-github-repo.md | 14 +- _chapters/ko/add-a-create-note-api.md | 6 +- .../ko/add-support-for-es6-es7-javascript.md | 2 +- _chapters/ko/add-the-session-to-the-state.md | 2 +- _chapters/ko/configure-aws-amplify.md | 8 +- ...ure-cognito-identity-pool-in-serverless.md | 6 +- ...nfigure-cognito-user-pool-in-serverless.md | 2 +- _chapters/ko/configure-s3-in-serverless.md | 2 +- _chapters/ko/configure-secrets-in-seed.md | 4 +- _chapters/ko/create-a-build-script.md | 2 +- .../ko/create-a-cognito-identity-pool.md | 4 +- _chapters/ko/create-a-dynamodb-table.md | 2 +- _chapters/ko/create-an-iam-user.md | 20 +- _chapters/ko/custom-domain-in-netlify.md | 2 +- _chapters/ko/deploy-again.md | 4 +- _chapters/ko/deploy-the-apis.md | 2 +- _chapters/ko/deploy-to-s3.md | 2 +- .../ko/give-feedback-while-logging-in.md | 2 +- .../ko/handle-api-gateway-cors-errors.md | 2 +- _chapters/ko/login-with-aws-cognito.md | 2 +- _chapters/ko/signup-with-aws-cognito.md | 2 +- _chapters/ko/test-the-apis.md | 12 +- _chapters/ko/test-the-configured-apis.md | 6 +- _chapters/ko/upload-a-file-to-s3.md | 2 +- ...vironment-variables-in-lambda-functions.md | 4 +- _chapters/ko/what-is-an-arn.md | 2 +- _chapters/list-all-the-notes.md | 15 +- _chapters/load-the-state-from-the-session.md | 106 +- _chapters/logic-errors-in-lambda-functions.md | 24 +- _chapters/login-with-aws-cognito.md | 45 +- .../pt/add-support-for-es6-es7-javascript.md | 2 +- _chapters/pt/create-a-dynamodb-table.md | 2 +- _chapters/purchase-a-domain-with-route-53.md | 14 +- _chapters/redirect-on-login-and-logout.md | 38 +- _chapters/redirect-on-login.md | 16 +- _chapters/render-the-note-form.md | 147 +- _chapters/report-api-errors-in-react.md | 66 +- _chapters/review-our-app-architecture.md | 6 +- _chapters/save-changes-to-a-note.md | 22 +- _chapters/secure-our-serverless-apis.md | 86 +- _chapters/setting-up-your-project-on-seed.md | 8 +- _chapters/setup-a-stripe-account.md | 18 +- _chapters/setup-an-error-boundary-in-react.md | 66 +- _chapters/setup-bootstrap.md | 19 +- _chapters/setup-custom-fonts.md | 8 +- .../setup-error-logging-in-serverless.md | 50 +- _chapters/setup-error-reporting-in-react.md | 27 +- _chapters/signup-with-aws-cognito.md | 46 +- _chapters/translations.md | 2 +- .../unexpected-errors-in-lambda-functions.md | 56 +- _chapters/unit-tests-in-serverless.md | 32 +- _chapters/upload-a-file-to-s3.md | 22 +- _chapters/use-the-redirect-routes.md | 21 +- _chapters/what-does-this-guide-cover.md | 116 +- _chapters/what-is-aws-cdk.md | 8 +- _chapters/what-is-aws-lambda.md | 21 +- _chapters/what-is-infrastructure-as-code.md | 8 +- _chapters/what-is-serverless.md | 10 +- _chapters/what-is-sst.md | 23 +- _chapters/who-is-this-guide-for.md | 22 +- _chapters/wrapping-up.md | 36 +- _config.yml | 23 +- _data/chapterlist.yml | 254 +- ...add-a-custom-domain-to-a-serverless-api.md | 10 +- ...uth0-authentication-to-a-serverless-api.md | 12 +- ...nito-authentication-to-a-serverless-api.md | 12 +- ...book-authentication-to-a-serverless-api.md | 12 +- ...acebook-login-to-your-cognito-user-pool.md | 18 +- ...-to-add-facebook-login-to-your-sst-apps.md | 26 +- ...-github-login-to-your-cognito-user-pool.md | 22 +- ...ogle-authentication-to-a-serverless-api.md | 12 +- ...-google-login-to-your-cognito-user-pool.md | 16 +- ...ow-to-add-google-login-to-your-sst-apps.md | 26 +- ...rization-with-auth0-to-a-serverless-api.md | 14 +- ...h-cognito-user-pool-to-a-serverless-api.md | 14 +- ...tter-authentication-to-a-serverless-api.md | 12 +- ...matically-resize-images-with-serverless.md | 4 +- ...crud-api-with-serverless-using-dynamodb.md | 16 +- ...to-create-a-flutter-app-with-serverless.md | 8 +- ...o-create-a-gatsbyjs-app-with-serverless.md | 14 +- ...-to-create-a-nextjs-app-with-serverless.md | 6 +- ...to-create-a-reactjs-app-with-serverless.md | 16 +- ...te-a-rest-api-in-golang-with-serverless.md | 2 +- ...-rest-api-in-typescript-with-serverless.md | 12 +- ...ow-to-create-a-rest-api-with-serverless.md | 12 +- ...serverless-graphql-api-with-aws-appsync.md | 20 +- ...-to-create-a-svelte-app-with-serverless.md | 12 +- ...w-to-create-a-vuejs-app-with-serverless.md | 14 +- ...-create-a-websocket-api-with-serverless.md | 12 +- ...o-create-an-angular-app-with-serverless.md | 26 +- ...e-an-apollo-graphql-api-with-serverless.md | 6 +- ...w-to-create-an-expo-app-with-serverless.md | 8 +- ...bug-lambda-functions-with-intellij-idea.md | 6 +- ...ambda-functions-with-visual-studio-code.md | 8 +- ...to-debug-lambda-functions-with-webstorm.md | 6 +- ...to-use-cron-jobs-in-your-serverless-app.md | 6 +- ...-datadog-to-monitor-your-serverless-app.md | 8 +- ...-to-use-dynamodb-in-your-serverless-app.md | 8 +- ...to-use-event-bus-in-your-serverless-app.md | 12 +- ...sis-data-streams-in-your-serverless-app.md | 12 +- ...se-lambda-layers-in-your-serverless-app.md | 4 +- ...e-lumigo-to-monitor-your-serverless-app.md | 8 +- ...o-validate-your-serverless-api-requests.md | 14 +- ...se-mongodb-atlas-in-your-serverless-app.md | 10 +- ...-use-planetscale-in-your-serverless-app.md | 6 +- ...o-use-postgresql-in-your-serverless-app.md | 8 +- ...w-to-use-pub-sub-in-your-serverless-app.md | 12 +- ...ow-to-use-queues-in-your-serverless-app.md | 10 +- ...e-sentry-to-monitor-your-serverless-app.md | 8 +- ...-thundra-to-monitor-your-serverless-app.md | 12 +- _includes/post-sidebar.html | 3 +- _includes/toc-sidebar.html | 2 +- _layouts/blog.html | 2 +- _layouts/post.html | 6 + _plugins/caution.rb | 22 + _plugins/change.rb | 6 +- _plugins/info.rb | 22 + _plugins/note.rb | 23 + _posts/2022-08-22-config.md | 6 +- _posts/2022-09-23-long-running-jobs.md | 6 +- _posts/2023-02-27-sst-v2.md | 2 +- _sass/guide.scss | 30 + _sass/lander.scss | 10 +- _sass/theme/_layout.scss | 6 + assets/app-update-live.png | Bin 169891 -> 0 bytes assets/chapter-translation-links.png | Bin 750120 -> 0 bytes assets/completed-app-desktop.png | Bin 622090 -> 617301 bytes assets/completed-app-mobile.png | Bin 722686 -> 520403 bytes .../add-iam-attach-policies-directly.png | Bin 0 -> 297602 bytes .../create-iam-user/add-iam-user-button.png | Bin 0 -> 234122 bytes .../fill-in-iam-user-details.png | Bin 0 -> 208869 bytes .../iam-access-credentials.png | Bin 0 -> 259689 bytes .../iam-access-key-purpose.png | Bin 0 -> 189456 bytes .../iam-access-key-secret-show.png | Bin 0 -> 253639 bytes .../iam-access-key-skip-tag-create.png | Bin 0 -> 191697 bytes assets/create-iam-user/iam-create-user.png | Bin 0 -> 218946 bytes .../create-iam-user/iam-success-view-user.png | Bin 0 -> 259728 bytes .../iam-user-access-key-purpose.png | Bin 0 -> 265233 bytes .../iam-user-add-admin-policy.png | Bin 0 -> 301802 bytes .../iam-user-create-access-key.png | Bin 0 -> 301098 bytes .../iam-user-security-credentials.png | Bin 0 -> 283673 bytes .../create-iam-user/search-to-iam-service.png | Bin 0 -> 302444 bytes .../select-iam-attach-policies-directly.png | Bin 0 -> 207630 bytes assets/custom-fonts-updated.png | Bin 535972 -> 0 bytes assets/empty-notes-page-loaded.png | Bin 471424 -> 0 bytes assets/how-to-get-help/sst-discord.png | Bin 0 -> 1342949 bytes .../{ => ko}/iam-user/add-iam-user-policy.png | Bin assets/{ => ko}/iam-user/add-iam-user.png | Bin .../{ => ko}/iam-user/added-admin-policy.png | Bin assets/{ => ko}/iam-user/added-iam-user.png | Bin .../iam-user/fill-in-iam-user-info.png | Bin .../iam-user/iam-user-credentials.png | Bin assets/{ => ko}/iam-user/review-iam-user.png | Bin .../{ => ko}/iam-user/select-iam-service.png | Bin assets/{ => ko}/iam-user/select-iam-users.png | Bin assets/{ => ko}/iam-user/skip-iam-tags.png | Bin assets/main.scss | 2 +- .../react-error-message.png | Bin 472334 -> 222362 bytes assets/new-create-react-app.png | Bin 535121 -> 0 bytes assets/new-homepage-loaded.png | Bin 474545 -> 0 bytes assets/new-note-page-added.png | Bin 495177 -> 0 bytes assets/note-page-deleting.png | Bin 507757 -> 0 bytes assets/notes-page-loaded.png | Bin 501043 -> 0 bytes assets/notes-page-saving.png | Bin 507394 -> 0 bytes assets/part2/app-update-live.png | Bin 0 -> 501916 bytes assets/part2/chapter-translation-links.png | Bin 0 -> 898847 bytes assets/part2/completed-app-mobile-lander.png | Bin 0 -> 290746 bytes assets/part2/custom-fonts-updated.png | Bin 0 -> 546396 bytes .../developer-section-in-stripe-dashboard.png | Bin 369665 -> 333968 bytes assets/part2/empty-notes-page-loaded.png | Bin 0 -> 504774 bytes assets/part2/new-homepage-loaded.png | Bin 0 -> 488987 bytes assets/part2/new-note-page-added.png | Bin 0 -> 516677 bytes assets/part2/new-vite-react-app.png | Bin 0 -> 553809 bytes assets/part2/note-page-deleting.png | Bin 0 -> 544361 bytes assets/part2/notes-page-loaded.png | Bin 0 -> 537566 bytes assets/part2/notes-page-saving.png | Bin 0 -> 554665 bytes .../part2/settings-screen-billing-success.png | Bin 513403 -> 550632 bytes .../settings-screen-with-billing-form.png | Bin 509976 -> 539586 bytes assets/part2/signup-page-added.png | Bin 0 -> 503769 bytes .../part2/signup-page-confirmation-code.png | Bin 0 -> 506876 bytes assets/part2/sst-console-api-tab.png | Bin 377821 -> 0 bytes assets/part2/sst-console-buckets-tab.png | Bin 349736 -> 0 bytes .../sst-console-cognito-create-new-user.png | Bin 369599 -> 0 bytes assets/part2/sst-console-cognito-new-user.png | Bin 368362 -> 0 bytes assets/part2/sst-console-cognito-tab.png | Bin 340977 -> 0 bytes .../sst-console-create-note-api-request.png | Bin 423599 -> 0 bytes .../sst-console-delete-note-api-request.png | Bin 457648 -> 0 bytes assets/part2/sst-console-dynamodb-tab.png | Bin 380350 -> 0 bytes .../sst-console-get-note-api-request.png | Bin 482412 -> 0 bytes .../sst-console-list-notes-api-request.png | Bin 478785 -> 0 bytes assets/part2/sst-console-local-tab.png | Bin 497497 -> 0 bytes assets/part2/sst-console-new-note.png | Bin 406922 -> 0 bytes .../sst-console-note-removed-in-dynamodb.png | Bin 370786 -> 0 bytes assets/part2/sst-console-prod-cognito-tab.png | Bin 734727 -> 0 bytes .../part2/sst-console-prod-dynamodb-tab.png | Bin 517280 -> 0 bytes .../part2/sst-console-prod-functions-tab.png | Bin 595743 -> 0 bytes assets/part2/sst-console-prod-logs.png | Bin 0 -> 736657 bytes assets/part2/sst-console-prod-resources.png | Bin 0 -> 633730 bytes assets/part2/sst-console-prod-stacks-tab.png | Bin 638601 -> 0 bytes .../part2/sst-console-test-user-new-note.png | Bin 400328 -> 0 bytes .../sst-console-update-note-api-request.png | Bin 458466 -> 0 bytes assets/part2/sst-hello-world-api-invoked.png | Bin 486031 -> 481905 bytes assets/serverless-stack-discourse-forums.png | Bin 390766 -> 0 bytes assets/signup-page-added.png | Bin 491328 -> 0 bytes etc/ebook/Makefile | 6 +- etc/ebook/cover.jpeg | Bin 271110 -> 185764 bytes guide.md | 6 +- package.json | 10 + pnpm-lock.yaml | 5197 +++++++++++++++++ resources/serverless-framework-vs-sst.md | 4 +- sst.config.ts | 24 + 367 files changed, 8189 insertions(+), 2352 deletions(-) rename {_chapters => _archives}/add-a-billing-api.md (98%) rename {_chapters => _archives}/add-a-create-note-api.md (98%) rename {_chapters => _archives}/add-a-delete-note-api.md (97%) rename {_chapters => _archives}/add-a-get-note-api.md (98%) rename {_chapters => _archives}/add-a-list-all-the-notes-api.md (97%) rename {_chapters => _archives}/add-an-update-note-api.md (98%) rename {_chapters => _archives}/add-support-for-es6-and-typescript.md (94%) rename {_chapters => _archives}/allow-users-to-change-passwords.md (99%) rename {_chapters => _archives}/allow-users-to-change-their-email.md (99%) rename {_chapters => _archives}/api-gateway-and-lambda-logs.md (99%) rename {_chapters => _archives}/backups-in-dynamodb.md (99%) rename {_chapters => _archives}/best-practices-for-building-serverless-apps.md (84%) rename {_chapters => _archives}/code-splitting-in-create-react-app.md (99%) rename {_chapters => _archives}/cognito-user-pool-vs-identity-pool.md (98%) rename {_chapters => _archives}/configure-cognito-identity-pool-in-serverless.md (94%) rename {_chapters => _archives}/configure-cognito-user-pool-in-serverless.md (94%) rename {_chapters => _archives}/configure-dynamodb-in-serverless.md (97%) rename {_chapters => _archives}/configure-multiple-aws-profiles.md (98%) rename {_chapters => _archives}/configure-s3-in-serverless.md (92%) rename {_chapters => _archives}/connect-to-api-gateway-with-iam-auth.md (98%) rename {_chapters => _archives}/create-a-cloudfront-distribution.md (98%) rename {_chapters => _archives}/create-a-cognito-identity-pool.md (95%) rename {_chapters => _archives}/create-a-cognito-test-user.md (96%) rename {_chapters => _archives}/create-a-cognito-user-pool.md (98%) rename {_chapters => _archives}/create-a-dynamodb-table.md (98%) rename {_chapters => _archives}/create-a-netlify-build-script.md (92%) rename {_chapters => _archives}/create-an-s3-bucket-for-file-uploads.md (94%) rename {_chapters => _archives}/create-an-s3-bucket.md (97%) rename {_chapters => _archives}/creating-a-ci-cd-pipeline-for-react.md (84%) rename {_chapters => _archives}/creating-feature-environments.md (99%) rename {_chapters => _archives}/creating-pull-request-environments.md (98%) rename {_chapters => _archives}/cross-stack-references-in-serverless.md (96%) rename {_chapters => _archives}/custom-domain-in-netlify.md (93%) rename {_chapters => _archives}/customize-the-serverless-iam-policy.md (99%) rename {_chapters => _archives}/debugging-serverless-api-issues.md (97%) rename {_chapters => _archives}/deploy-a-serverless-app-with-dependencies.md (96%) rename {_chapters => _archives}/deploy-the-api-services-repo.md (98%) rename {_chapters => _archives}/deploy-the-apis.md (97%) rename {_chapters => _archives}/deploy-the-resources-repo.md (97%) rename {_chapters => _archives}/deploy-to-s3.md (93%) rename {_chapters => _archives}/deploy-updates.md (97%) rename {_chapters => _archives}/deploy-your-hello-world-api.md (94%) rename {_chapters => _archives}/deploy-your-serverless-infrastructure.md (88%) rename {_chapters => _archives}/deploying-a-react-app-to-aws.md (90%) rename {_chapters => _archives}/deploying-a-react-app-to-netlify.md (87%) rename {_chapters => _archives}/deploying-only-updated-services.md (98%) rename {_chapters => _archives}/deploying-to-multiple-aws-accounts.md (98%) rename {_chapters => _archives}/dynamically-generate-social-share-images-with-serverless.md (99%) rename {_chapters => _archives}/environments-in-create-react-app.md (97%) rename {_chapters => _archives}/environments-in-serverless-apps.md (97%) rename {_chapters => _archives}/facebook-login-with-cognito-using-aws-amplify.md (98%) rename {_chapters => _archives}/frontend-workflow-in-netlify.md (97%) rename {_chapters => _archives}/handle-api-gateway-cors-errors.md (98%) rename {_chapters => _archives}/handle-forgot-and-reset-password.md (99%) rename {_chapters => _archives}/how-to-add-authentication-to-a-serverless-app.md (95%) rename {_chapters => _archives}/invoke-api-gateway-endpoints-locally.md (98%) rename {_chapters => _archives}/invoke-lambda-functions-locally.md (98%) rename {_chapters => _archives}/load-secrets-from-env.md (96%) rename {_chapters => _archives}/manage-aws-accounts-using-aws-organizations.md (98%) rename {_chapters => _archives}/manage-environment-related-config.md (96%) rename {_chapters => _archives}/manage-environments-in-create-react-app.md (98%) rename {_chapters => _archives}/manage-user-accounts-in-aws-amplify.md (95%) rename {_chapters => _archives}/mapping-cognito-identity-id-and-user-pool-id.md (96%) rename {_chapters => _archives}/monitor-usage-for-environments.md (97%) rename {_chapters => _archives}/organizing-serverless-projects.md (98%) rename {_chapters => _archives}/package-lambdas-with-serverless-bundle.md (98%) rename {_chapters => _archives}/parameterize-serverless-resources-names.md (98%) rename {_chapters => _archives}/promoting-to-production.md (98%) rename {_chapters => _archives}/rollback-changes.md (96%) rename {_chapters => _archives}/secure-the-apis.md (96%) rename {_chapters => _archives}/serverless-environment-variables.md (98%) rename {_chapters => _archives}/serverless-nodejs-starter.md (97%) rename {_chapters => _archives}/set-custom-domains-through-seed.md (97%) rename {_chapters => _archives}/setting-serverless-environments-variables-in-a-react-app.md (86%) rename {_chapters => _archives}/setting-up-your-project-on-netlify.md (97%) rename {_chapters => _archives}/setup-a-custom-domain-with-ssl.md (97%) rename {_chapters => _archives}/setup-the-serverless-framework.md (98%) rename {_chapters => _archives}/setup-www-domain-redirect.md (98%) rename {_chapters => _archives}/setup-your-domain-with-cloudfront.md (98%) rename {_chapters => _archives}/share-an-api-endpoint-between-services.md (98%) rename {_chapters => _archives}/share-code-between-services.md (98%) rename {_chapters => _archives}/share-route-53-domains-across-aws-accounts.md (97%) rename {_chapters => _archives}/stages-in-serverless-framework.md (96%) rename {_chapters => _archives}/storing-secrets-in-serverless-apps.md (97%) rename {_chapters => _archives}/structure-environments-across-aws-accounts.md (96%) rename {_chapters => _archives}/test-the-apis.md (92%) rename {_chapters => _archives}/test-the-billing-api.md (98%) rename {_chapters => _archives}/tracing-serverless-apps-with-x-ray.md (98%) rename {_chapters => _archives}/understanding-react-hooks.md (99%) rename {_chapters => _archives}/using-aws-cdk-with-serverless-framework.md (97%) rename {_chapters => _archives}/using-cognito-to-add-authentication-to-a-serverless-app.md (98%) rename {_chapters => _archives}/using-lerna-and-yarn-workspaces-with-serverless.md (97%) rename {_chapters => _archives}/what-is-an-arn.md (89%) rename {_chapters => _archives}/what-is-aws-appsync.md (99%) rename {_chapters => _archives}/what-is-iam.md (85%) rename {_chapters => _archives}/working-on-serverless-apps.md (66%) rename {_chapters => _archives}/working-with-3rd-party-apis.md (96%) rename {_chapters => _archives}/wrapping-up-the-best-practices.md (96%) create mode 100644 _plugins/caution.rb create mode 100644 _plugins/info.rb create mode 100644 _plugins/note.rb delete mode 100644 assets/app-update-live.png delete mode 100644 assets/chapter-translation-links.png create mode 100644 assets/create-iam-user/add-iam-attach-policies-directly.png create mode 100644 assets/create-iam-user/add-iam-user-button.png create mode 100644 assets/create-iam-user/fill-in-iam-user-details.png create mode 100644 assets/create-iam-user/iam-access-credentials.png create mode 100644 assets/create-iam-user/iam-access-key-purpose.png create mode 100644 assets/create-iam-user/iam-access-key-secret-show.png create mode 100644 assets/create-iam-user/iam-access-key-skip-tag-create.png create mode 100644 assets/create-iam-user/iam-create-user.png create mode 100644 assets/create-iam-user/iam-success-view-user.png create mode 100644 assets/create-iam-user/iam-user-access-key-purpose.png create mode 100644 assets/create-iam-user/iam-user-add-admin-policy.png create mode 100644 assets/create-iam-user/iam-user-create-access-key.png create mode 100644 assets/create-iam-user/iam-user-security-credentials.png create mode 100644 assets/create-iam-user/search-to-iam-service.png create mode 100644 assets/create-iam-user/select-iam-attach-policies-directly.png delete mode 100644 assets/custom-fonts-updated.png delete mode 100644 assets/empty-notes-page-loaded.png create mode 100644 assets/how-to-get-help/sst-discord.png rename assets/{ => ko}/iam-user/add-iam-user-policy.png (100%) rename assets/{ => ko}/iam-user/add-iam-user.png (100%) rename assets/{ => ko}/iam-user/added-admin-policy.png (100%) rename assets/{ => ko}/iam-user/added-iam-user.png (100%) rename assets/{ => ko}/iam-user/fill-in-iam-user-info.png (100%) rename assets/{ => ko}/iam-user/iam-user-credentials.png (100%) rename assets/{ => ko}/iam-user/review-iam-user.png (100%) rename assets/{ => ko}/iam-user/select-iam-service.png (100%) rename assets/{ => ko}/iam-user/select-iam-users.png (100%) rename assets/{ => ko}/iam-user/skip-iam-tags.png (100%) delete mode 100644 assets/new-create-react-app.png delete mode 100644 assets/new-homepage-loaded.png delete mode 100644 assets/new-note-page-added.png delete mode 100644 assets/note-page-deleting.png delete mode 100644 assets/notes-page-loaded.png delete mode 100644 assets/notes-page-saving.png create mode 100644 assets/part2/app-update-live.png create mode 100644 assets/part2/chapter-translation-links.png create mode 100644 assets/part2/completed-app-mobile-lander.png create mode 100644 assets/part2/custom-fonts-updated.png create mode 100644 assets/part2/empty-notes-page-loaded.png create mode 100644 assets/part2/new-homepage-loaded.png create mode 100644 assets/part2/new-note-page-added.png create mode 100644 assets/part2/new-vite-react-app.png create mode 100644 assets/part2/note-page-deleting.png create mode 100644 assets/part2/notes-page-loaded.png create mode 100644 assets/part2/notes-page-saving.png create mode 100644 assets/part2/signup-page-added.png create mode 100644 assets/part2/signup-page-confirmation-code.png delete mode 100644 assets/part2/sst-console-api-tab.png delete mode 100644 assets/part2/sst-console-buckets-tab.png delete mode 100644 assets/part2/sst-console-cognito-create-new-user.png delete mode 100644 assets/part2/sst-console-cognito-new-user.png delete mode 100644 assets/part2/sst-console-cognito-tab.png delete mode 100644 assets/part2/sst-console-create-note-api-request.png delete mode 100644 assets/part2/sst-console-delete-note-api-request.png delete mode 100644 assets/part2/sst-console-dynamodb-tab.png delete mode 100644 assets/part2/sst-console-get-note-api-request.png delete mode 100644 assets/part2/sst-console-list-notes-api-request.png delete mode 100644 assets/part2/sst-console-local-tab.png delete mode 100644 assets/part2/sst-console-new-note.png delete mode 100644 assets/part2/sst-console-note-removed-in-dynamodb.png delete mode 100644 assets/part2/sst-console-prod-cognito-tab.png delete mode 100644 assets/part2/sst-console-prod-dynamodb-tab.png delete mode 100644 assets/part2/sst-console-prod-functions-tab.png create mode 100644 assets/part2/sst-console-prod-logs.png create mode 100644 assets/part2/sst-console-prod-resources.png delete mode 100644 assets/part2/sst-console-prod-stacks-tab.png delete mode 100644 assets/part2/sst-console-test-user-new-note.png delete mode 100644 assets/part2/sst-console-update-note-api-request.png delete mode 100644 assets/serverless-stack-discourse-forums.png delete mode 100644 assets/signup-page-added.png create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 sst.config.ts diff --git a/.gitignore b/.gitignore index f2912a661..bef1f633c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ Session.vim +.sst/ +node_modules/ _site .sw* .*.sw* @@ -7,3 +9,4 @@ _site .jekyll-cache/ .DS_Store vendor/ +.idea/ diff --git a/Gemfile.lock b/Gemfile.lock index bb73cc321..caf647bd6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,6 +72,7 @@ GEM PLATFORMS arm64-darwin-21 + x86_64-darwin-22 DEPENDENCIES jekyll (= 4.1.1) @@ -81,7 +82,7 @@ DEPENDENCIES webrick (~> 1.7) RUBY VERSION - ruby 3.0.4p208 + ruby 2.6.10p210 BUNDLED WITH - 2.3.20 + 2.4.5 diff --git a/README.md b/README.md index b6dce0343..0126142c4 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -

+

SST

+

Discord Twitter diff --git a/_chapters/add-a-billing-api.md b/_archives/add-a-billing-api.md similarity index 98% rename from _chapters/add-a-billing-api.md rename to _archives/add-a-billing-api.md index 5098afdea..6d61fbfd2 100644 --- a/_chapters/add-a-billing-api.md +++ b/_archives/add-a-billing-api.md @@ -6,6 +6,7 @@ lang: en description: We are going to create a Lambda function for our serverless billing API. It will take the Stripe token that is passed in from our app and use the Stripe JS SDK to process the payment. ref: add-a-billing-api comments_id: add-a-billing-api/170 +redirect_from: /chapters/add-a-billing-api.html --- Now let's get started with creating our billing API. It is going to take a Stripe token and the number of notes the user wants to store. diff --git a/_chapters/add-a-create-note-api.md b/_archives/add-a-create-note-api.md similarity index 98% rename from _chapters/add-a-create-note-api.md rename to _archives/add-a-create-note-api.md index 453b20a48..4e88e6740 100644 --- a/_chapters/add-a-create-note-api.md +++ b/_archives/add-a-create-note-api.md @@ -6,6 +6,7 @@ lang: en ref: add-a-create-note-api description: To allow users to create notes in our note taking app, we are going to add a create note POST API. To do this we are going to add a new Lambda function to our Serverless Framework project. The Lambda function will save the note to our DynamoDB table and return the newly created note. comments_id: add-a-create-note-api/125 +redirect_from: /chapters/add-a-create-note-api.html --- Let's get started on our backend by first adding an API to create a note. This API will take the note object as the input and store it in the database with a new id. The note object will contain the `content` field (the content of the note) and an `attachment` field (the URL to the uploaded file). @@ -67,7 +68,7 @@ There are some helpful comments in the code but we are doing a few simple things - The AWS JS SDK assumes the region based on the current region of the Lambda function. So if your DynamoDB table is in a different region, make sure to set it by calling `AWS.config.update({ region: "my-region" });` before initializing the DynamoDB client. - Parse the input from the `event.body`. This represents the HTTP request body. - It contains the contents of the note, as a string — `content`. -- It also contains an `attachment`, if one exists. It's the filename of file that has been uploaded to [our S3 bucket]({% link _chapters/create-an-s3-bucket-for-file-uploads.md %}). +- It also contains an `attachment`, if one exists. It's the filename of file that has been uploaded to [our S3 bucket]({% link _archives/create-an-s3-bucket-for-file-uploads.md %}). - We read the name of our DynamoDB table from the environment variable using `process.env.tableName`. We'll be setting this in our `serverless.yml` below. We do this so we won't have to hardcode it in every function. - The `userId` is the id for the author of the note. For now we are hardcoding it to `123`. Later we'll be setting this based on the authenticated user. - Make a call to DynamoDB to put a new object with a generated `noteId` and the current date as the `createdAt`. @@ -129,7 +130,7 @@ functions: method: post ``` -Here we are adding our newly added create function to the configuration. We specify that it handles `post` requests at the `/notes` endpoint. This pattern of using a single Lambda function to respond to a single HTTP event is very much like the [Microservices architecture](https://en.wikipedia.org/wiki/Microservices). We discuss this and a few other patterns in the chapter on [organizing Serverless Framework projects]({% link _chapters/organizing-serverless-projects.md %}). +Here we are adding our newly added create function to the configuration. We specify that it handles `post` requests at the `/notes` endpoint. This pattern of using a single Lambda function to respond to a single HTTP event is very much like the [Microservices architecture](https://en.wikipedia.org/wiki/Microservices). We discuss this and a few other patterns in the chapter on [organizing Serverless Framework projects]({% link _archives/organizing-serverless-projects.md %}). The `environment:` block allows us to define environment variables for our Lambda function. These are made available under the `process.env` Node.js variable. In our specific case, we are using `process.env.tableName` to access the name of our DynamoDB table. @@ -167,7 +168,7 @@ If you have multiple profiles for your AWS SDK credentials, you will need to exp $ AWS_PROFILE=myProfile serverless invoke local --function create --path mocks/create-event.json ``` -Where `myProfile` is the name of the AWS profile you want to use. If you need more info on how to work with AWS profiles in Serverless, refer to our [Configure multiple AWS profiles]({% link _chapters/configure-multiple-aws-profiles.md %}) chapter. +Where `myProfile` is the name of the AWS profile you want to use. If you need more info on how to work with AWS profiles in Serverless, refer to our [Configure multiple AWS profiles]({% link _archives/configure-multiple-aws-profiles.md %}) chapter. The response should look similar to this. diff --git a/_chapters/add-a-delete-note-api.md b/_archives/add-a-delete-note-api.md similarity index 97% rename from _chapters/add-a-delete-note-api.md rename to _archives/add-a-delete-note-api.md index e50bbfd71..38dda7fad 100644 --- a/_chapters/add-a-delete-note-api.md +++ b/_archives/add-a-delete-note-api.md @@ -6,6 +6,7 @@ lang: en ref: add-a-delete-note-api description: To allow users to delete their notes in our note taking app, we are going to add a DELETE note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will delete a user’s note in the DynamoDB table. comments_id: add-a-delete-note-api/153 +redirect_from: /chapters/add-a-delete-note-api.html --- Finally, we are going to create an API that allows a user to delete a given note. diff --git a/_chapters/add-a-get-note-api.md b/_archives/add-a-get-note-api.md similarity index 98% rename from _chapters/add-a-get-note-api.md rename to _archives/add-a-get-note-api.md index 198a3fdf2..10b2b6165 100644 --- a/_chapters/add-a-get-note-api.md +++ b/_archives/add-a-get-note-api.md @@ -6,6 +6,7 @@ lang: en ref: add-a-get-note-api description: To allow users to retrieve a note in our note taking app, we are going to add a GET note API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve the note from our DynamoDB table. comments_id: add-a-get-note-api/132 +redirect_from: /chapters/add-a-get-note-api.html --- Now that we created a note and saved it to our database. Let's add an API to retrieve a note given its id. diff --git a/_chapters/add-a-list-all-the-notes-api.md b/_archives/add-a-list-all-the-notes-api.md similarity index 97% rename from _chapters/add-a-list-all-the-notes-api.md rename to _archives/add-a-list-all-the-notes-api.md index b39dbaa69..2cd4df56b 100644 --- a/_chapters/add-a-list-all-the-notes-api.md +++ b/_archives/add-a-list-all-the-notes-api.md @@ -6,6 +6,7 @@ lang: en ref: add-a-list-all-the-notes-api description: To allow users to retrieve their notes in our note taking app, we are going to add a list note GET API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will retrieve all the user’s notes from the DynamoDB table. comments_id: add-a-list-all-the-notes-api/147 +redirect_from: /chapters/add-a-list-all-the-notes-api.html --- Now we are going to add an API that returns a list of all the notes a user has. diff --git a/_chapters/add-an-update-note-api.md b/_archives/add-an-update-note-api.md similarity index 98% rename from _chapters/add-an-update-note-api.md rename to _archives/add-an-update-note-api.md index b77309bec..44140e949 100644 --- a/_chapters/add-an-update-note-api.md +++ b/_archives/add-an-update-note-api.md @@ -6,6 +6,7 @@ lang: en ref: add-an-update-note-api description: To allow users to update their notes in our note taking app, we are going to add an update note PUT API. To do this we will add a new Lambda function to our Serverless Framework project. The Lambda function will update a user’s note in the DynamoDB table. comments_id: add-an-update-note-api/144 +redirect_from: /chapters/add-an-update-note-api.html --- Now let's create an API that allows a user to update a note with a new note object given its id. diff --git a/_chapters/add-support-for-es6-and-typescript.md b/_archives/add-support-for-es6-and-typescript.md similarity index 94% rename from _chapters/add-support-for-es6-and-typescript.md rename to _archives/add-support-for-es6-and-typescript.md index 9b6a809f4..7a1cdf430 100644 --- a/_chapters/add-support-for-es6-and-typescript.md +++ b/_archives/add-support-for-es6-and-typescript.md @@ -4,11 +4,12 @@ title: Add Support for ES6 and TypeScript date: 2016-12-29 12:00:00 lang: en ref: add-support-for-es6-and-typescript +description: AWS Lambda supports Node.js v10.x and v12.x. However, to use ES 6 features or TypeScript in our Serverless Framework project we need to use Babel, Webpack 5, and a ton of other packages. We can do this by using the serverless-bundle plugin to our project. +comments_id: add-support-for-es6-es7-javascript/128 redirect_from: - /chapters/add-support-for-es6-javascript.html - /chapters/add-support-for-es6-es7-javascript.html -description: AWS Lambda supports Node.js v10.x and v12.x. However, to use ES 6 features or TypeScript in our Serverless Framework project we need to use Babel, Webpack 5, and a ton of other packages. We can do this by using the serverless-bundle plugin to our project. -comments_id: add-support-for-es6-es7-javascript/128 + - /chapters/add-support-for-es6-and-typescript.html --- AWS Lambda supports Node.js v10.x, v12.x, and v14.x. However, the supported syntax is a little different when compared to the more advanced ECMAScript flavor of JavaScript that our frontend React app supports. It makes sense to use similar ES features across both parts of the project – specifically, we'll be relying on ES imports/exports in our handler functions. @@ -26,7 +27,7 @@ To help with this we created, [`serverless-bundle`](https://github.com/AnomalyIn - Supports transpiling unit tests with [babel-jest](https://github.com/facebook/jest/tree/master/packages/babel-jest) - Source map support for proper error messages -It's automatically included in the starter project we used in the previous chapter — [`serverless-nodejs-starter`]({% link _chapters/serverless-nodejs-starter.md %}). For TypeScript, we have a starter for that as well — [`serverless-typescript-starter`](https://github.com/AnomalyInnovations/serverless-typescript-starter). +It's automatically included in the starter project we used in the previous chapter — [`serverless-nodejs-starter`]({% link _archives/serverless-nodejs-starter.md %}). For TypeScript, we have a starter for that as well — [`serverless-typescript-starter`](https://github.com/AnomalyInnovations/serverless-typescript-starter). However, if you are looking to add ES6 and TypeScript support to your existing Serverless Framework projects, you can do this by installing [serverless-bundle](https://github.com/AnomalyInnovations/serverless-bundle): @@ -89,7 +90,7 @@ You should see something like this in your terminal. In the above command we are asking Serverless Framework to (locally) invoke a Lambda function called `hello`. This in turn will run the `hello` method that we are exporting in our `handler.js`. -Here we are directly invoking the Lambda function. Though once deployed, we'll be invoking this function through the `/hello` API endpoint (as we [talked about in the last chapter]({% link _chapters/setup-the-serverless-framework.md %})). +Here we are directly invoking the Lambda function. Though once deployed, we'll be invoking this function through the `/hello` API endpoint (as we [talked about in the last chapter]({% link _archives/setup-the-serverless-framework.md %})). Now we are almost ready to deploy our Lambda function and API. But before we do that let's quickly look at one of the other things that's been set up for us in this starter project. diff --git a/_chapters/allow-users-to-change-passwords.md b/_archives/allow-users-to-change-passwords.md similarity index 99% rename from _chapters/allow-users-to-change-passwords.md rename to _archives/allow-users-to-change-passwords.md index a253ea591..41631e3c6 100644 --- a/_chapters/allow-users-to-change-passwords.md +++ b/_archives/allow-users-to-change-passwords.md @@ -5,6 +5,7 @@ description: Use the AWS Amplify Auth.changePassword method to support change pa date: 2018-04-15 00:00:00 code: user-management comments_id: allow-users-to-change-passwords/507 +redirect_from: /chapters/allow-users-to-change-passwords.html --- For our [Serverless notes app](https://demo.sst.dev), we want to allow our users to change their password. Recall that we are using Cognito to manage our users and AWS Amplify in our React app. In this chapter we will look at how to do that. diff --git a/_chapters/allow-users-to-change-their-email.md b/_archives/allow-users-to-change-their-email.md similarity index 99% rename from _chapters/allow-users-to-change-their-email.md rename to _archives/allow-users-to-change-their-email.md index ece2cbd98..cc8efe3bc 100644 --- a/_chapters/allow-users-to-change-their-email.md +++ b/_archives/allow-users-to-change-their-email.md @@ -5,6 +5,7 @@ description: Use the AWS Amplify Auth.updateUserAttributes method to support cha date: 2018-04-16 00:00:00 code: user-management comments_id: allow-users-to-change-their-email/508 +redirect_from: /chapters/allow-users-to-change-their-email.html --- We want the users of our [Serverless notes app](https://demo.sst.dev) to be able to change their email. Recall that we are using Cognito to manage our users and AWS Amplify in our React app. In this chapter we will look at how to do that. diff --git a/_chapters/api-gateway-and-lambda-logs.md b/_archives/api-gateway-and-lambda-logs.md similarity index 99% rename from _chapters/api-gateway-and-lambda-logs.md rename to _archives/api-gateway-and-lambda-logs.md index 425e99932..874a8aae4 100644 --- a/_chapters/api-gateway-and-lambda-logs.md +++ b/_archives/api-gateway-and-lambda-logs.md @@ -4,6 +4,7 @@ title: API Gateway and Lambda Logs description: To view logs for your serverless APIs on AWS, CloudWatch needs to be enabled for API Gateway and Lambda. CloudWatch logs are ordered by Log Groups and Log Stream. Lambda CloudWatch logs can also be viewed using the Serverless CLI with the “serverless logs” command. date: 2018-04-03 00:00:00 comments_id: api-gateway-and-lambda-logs/31 +redirect_from: /chapters/api-gateway-and-lambda-logs.html --- Logging is an essential part of building backends and it is no different for a serverless API. It gives us visibility into how we are processing and responding to incoming requests. diff --git a/_chapters/backups-in-dynamodb.md b/_archives/backups-in-dynamodb.md similarity index 99% rename from _chapters/backups-in-dynamodb.md rename to _archives/backups-in-dynamodb.md index 257095292..bb5420fa3 100644 --- a/_chapters/backups-in-dynamodb.md +++ b/_archives/backups-in-dynamodb.md @@ -4,6 +4,7 @@ title: Backups in DynamoDB date: 2018-04-06 12:00:00 description: Amazon DynamoDB allows you to create On-demand backups and enable Point-in-time recovery with a single click. Backups are fully-managed, extremely fast, and do not impact performance. In this chapter we look at the two ways to backup and restore DynamoDB tables. comments_id: backups-in-dynamodb/705 +redirect_from: /chapters/backups-in-dynamodb.html --- An important (yet overlooked) aspect of having a database powering your web application are, backups! In this chapter we are going to take a look at how to configure backups for your DynamoDB tables. diff --git a/_chapters/best-practices-for-building-serverless-apps.md b/_archives/best-practices-for-building-serverless-apps.md similarity index 84% rename from _chapters/best-practices-for-building-serverless-apps.md rename to _archives/best-practices-for-building-serverless-apps.md index 899c702e2..37fffa566 100644 --- a/_chapters/best-practices-for-building-serverless-apps.md +++ b/_archives/best-practices-for-building-serverless-apps.md @@ -4,6 +4,7 @@ title: Best Practices for Building Serverless Apps description: In this section of the guide we'll be covering the best practices for developing and maintaining large Serverless applications. It builds on what we've covered so far and it extends the demo notes app that we built in the first section. It's intended for teams as opposed to individual developers. It's meant to give you a foundation that scales as your app (and team) grows. date: 2019-10-03 00:00:00 comments_id: best-practices-for-building-serverless-apps/1315 +redirect_from: /chapters/best-practices-for-building-serverless-apps.html --- In this section of the guide we'll be covering the best practices for developing and maintaining large Serverless applications. It builds on what we've covered so far and it extends the [demo notes app]({{ site.demo_url }}) that we built in the first section. It's intended for teams as opposed to individual developers. It's meant to give you a foundation that scales as your app (and team) grows. @@ -41,23 +42,23 @@ Here is a rough rundown of the topics covered in this section of the guide. We are covering primarily the backend Serverless portion. The frontend flow works relatively the same way as what we covered in the first section. We also found that there is a distinct lack of best practices for building Serverless backends as opposed to React apps. -- [Organizing large Serverless apps]({% link _chapters/organizing-serverless-projects.md %}) - - [Sharing resources using cross-stack references]({% link _chapters/cross-stack-references-in-serverless.md %}) - - [Sharing code between services]({% link _chapters/share-code-between-services.md %}) - - [Sharing API endpoints across services]({% link _chapters/share-an-api-endpoint-between-services.md %}) -- [Configuring environments]({% link _chapters/environments-in-serverless-apps.md %}) - - [Using separate AWS accounts to manage environments ]({% link _chapters/structure-environments-across-aws-accounts.md %}) - - [Parameterizing resource names]({% link _chapters/parameterize-serverless-resources-names.md %}) - - [Managing environment specific configs]({% link _chapters/manage-environment-related-config.md %}) - - [Best practices for handling secrets]({% link _chapters/storing-secrets-in-serverless-apps.md %}) - - [Sharing domains across environments]({% link _chapters/share-route-53-domains-across-aws-accounts.md %}) -- [Development lifecycle]({% link _chapters/working-on-serverless-apps.md %}) - - [Working locally]({% link _chapters/invoke-api-gateway-endpoints-locally.md %}) - - [Creating feature environments]({% link _chapters/creating-feature-environments.md %}) - - [Creating pull request environments]({% link _chapters/creating-pull-request-environments.md %}) - - [Promoting to production]({% link _chapters/promoting-to-production.md %}) - - [Handling rollbacks]({% link _chapters/rollback-changes.md %}) -- [Using AWS X-Ray to trace Lambda functions]({% link _chapters/tracing-serverless-apps-with-x-ray.md %}) +- [Organizing large Serverless apps]({% link _archives/organizing-serverless-projects.md %}) + - [Sharing resources using cross-stack references]({% link _archives/cross-stack-references-in-serverless.md %}) + - [Sharing code between services]({% link _archives/share-code-between-services.md %}) + - [Sharing API endpoints across services]({% link _archives/share-an-api-endpoint-between-services.md %}) +- [Configuring environments]({% link _archives/environments-in-serverless-apps.md %}) + - [Using separate AWS accounts to manage environments ]({% link _archives/structure-environments-across-aws-accounts.md %}) + - [Parameterizing resource names]({% link _archives/parameterize-serverless-resources-names.md %}) + - [Managing environment specific configs]({% link _archives/manage-environment-related-config.md %}) + - [Best practices for handling secrets]({% link _archives/storing-secrets-in-serverless-apps.md %}) + - [Sharing domains across environments]({% link _archives/share-route-53-domains-across-aws-accounts.md %}) +- [Development lifecycle]({% link _archives/working-on-serverless-apps.md %}) + - [Working locally]({% link _archives/invoke-api-gateway-endpoints-locally.md %}) + - [Creating feature environments]({% link _archives/creating-feature-environments.md %}) + - [Creating pull request environments]({% link _archives/creating-pull-request-environments.md %}) + - [Promoting to production]({% link _archives/promoting-to-production.md %}) + - [Handling rollbacks]({% link _archives/rollback-changes.md %}) +- [Using AWS X-Ray to trace Lambda functions]({% link _archives/tracing-serverless-apps-with-x-ray.md %}) We think these concepts should be a good starting point for your projects and you should be able to adapt them to fit your use case! diff --git a/_chapters/code-splitting-in-create-react-app.md b/_archives/code-splitting-in-create-react-app.md similarity index 99% rename from _chapters/code-splitting-in-create-react-app.md rename to _archives/code-splitting-in-create-react-app.md index 49f9b6331..ce8723dbd 100644 --- a/_chapters/code-splitting-in-create-react-app.md +++ b/_archives/code-splitting-in-create-react-app.md @@ -4,6 +4,7 @@ title: Code Splitting in Create React App description: Code splitting in Create React App is an easy way to reduce the size of your React.js app bundle. To do this in an app using React Router v5, we can asynchronously load our routes using the dynamic import() method that Create React App supports. date: 2018-04-17 00:00:00 comments_id: code-splitting-in-create-react-app/98 +redirect_from: /chapters/code-splitting-in-create-react-app.html --- Code Splitting is not a necessary step for building React apps. But feel free to follow along if you are curious about what Code Splitting is and how it can help larger React apps. diff --git a/_chapters/cognito-user-pool-vs-identity-pool.md b/_archives/cognito-user-pool-vs-identity-pool.md similarity index 98% rename from _chapters/cognito-user-pool-vs-identity-pool.md rename to _archives/cognito-user-pool-vs-identity-pool.md index cd7a91e39..3ac0a71ef 100644 --- a/_chapters/cognito-user-pool-vs-identity-pool.md +++ b/_archives/cognito-user-pool-vs-identity-pool.md @@ -6,6 +6,7 @@ lang: en ref: cognito-user-pool-vs-identity-pool description: Amazon Cognito User Pool is a service that helps manage your users and the sign-up and sign-in functionality for your mobile or web app. Cognito Identity Pool or Cognito Federated Identities is a service that uses identity providers (like Google, Facebook, or Cognito User Pool) to secure access to other AWS resources. comments_id: cognito-user-pool-vs-identity-pool/146 +redirect_from: /chapters/cognito-user-pool-vs-identity-pool.html --- We often get questions about the differences between the Cognito User Pool and the Identity Pool, so it is worth covering in detail. The two can seem a bit similar in function and it is not entirely clear what they are for. Let's first start with the official definitions. diff --git a/_chapters/configure-cognito-identity-pool-in-serverless.md b/_archives/configure-cognito-identity-pool-in-serverless.md similarity index 94% rename from _chapters/configure-cognito-identity-pool-in-serverless.md rename to _archives/configure-cognito-identity-pool-in-serverless.md index d74a4e184..0211e8cba 100644 --- a/_chapters/configure-cognito-identity-pool-in-serverless.md +++ b/_archives/configure-cognito-identity-pool-in-serverless.md @@ -5,8 +5,10 @@ date: 2018-03-02 00:00:00 lang: en description: We can define our Cognito Identity Pool using the Infrastructure as Code pattern by using CloudFormation in our serverless.yml. We are going to set the User Pool as the Cognito Identity Provider. And define the Auth Role with a policy allowing access to our S3 Bucket and API Gateway endpoint. ref: configure-cognito-identity-pool-in-serverless -redirect_from: /chapters/cognito-as-a-serverless-service.html comments_id: configure-cognito-identity-pool-in-serverless/165 +redirect_from: + - /chapters/configure-cognito-identity-pool-in-serverless.html + - /chapters/cognito-as-a-serverless-service.html --- If you recall from the earlier part of this section, we used the Cognito Identity Pool as a way to control which AWS resources our logged-in users will have access to. We also tie in our Cognito User Pool as our authentication provider. @@ -110,7 +112,7 @@ Outputs: Ref: CognitoIdentityPool ``` -While it looks like there's a whole lot going on here, it's pretty much exactly what we did back in the [Create a Cognito identity pool]({% link _chapters/create-a-cognito-identity-pool.md %}) chapter. It's just that CloudFormation can be a bit verbose and can end up looking a bit intimidating. +While it looks like there's a whole lot going on here, it's pretty much exactly what we did back in the [Create a Cognito identity pool]({% link _archives/create-a-cognito-identity-pool.md %}) chapter. It's just that CloudFormation can be a bit verbose and can end up looking a bit intimidating. Let's quickly go over the various sections of this configuration: @@ -118,11 +120,11 @@ Let's quickly go over the various sections of this configuration: 2. We specify that we only want logged in users by adding `AllowUnauthenticatedIdentities: false`. -3. Next we state that we want to use our User Pool as the identity provider. We are doing this specifically using the `Ref: CognitoUserPoolClient` line. If you refer back to the [Configure Cognito User Pool in Serverless]({% link _chapters/configure-cognito-user-pool-in-serverless.md %}) chapter, you'll notice we have a block under `CognitoUserPoolClient` that we are referencing here. +3. Next we state that we want to use our User Pool as the identity provider. We are doing this specifically using the `Ref: CognitoUserPoolClient` line. If you refer back to the [Configure Cognito User Pool in Serverless]({% link _archives/configure-cognito-user-pool-in-serverless.md %}) chapter, you'll notice we have a block under `CognitoUserPoolClient` that we are referencing here. 4. We then attach an IAM role to our authenticated users. -5. We add the various parts to this role. This is exactly what we use in the [Create a Cognito identity pool]({% link _chapters/create-a-cognito-identity-pool.md %}) chapter. It just needs to be formatted this way to work with CloudFormation. +5. We add the various parts to this role. This is exactly what we use in the [Create a Cognito identity pool]({% link _archives/create-a-cognito-identity-pool.md %}) chapter. It just needs to be formatted this way to work with CloudFormation. 6. The `ApiGatewayRestApi` ref that you might notice is generated by Serverless Framework when you define an API endpoint in your `serverless.yml`. So in this case, we are referencing the API resource that we are creating. diff --git a/_chapters/configure-cognito-user-pool-in-serverless.md b/_archives/configure-cognito-user-pool-in-serverless.md similarity index 94% rename from _chapters/configure-cognito-user-pool-in-serverless.md rename to _archives/configure-cognito-user-pool-in-serverless.md index acc333261..72336f2aa 100644 --- a/_chapters/configure-cognito-user-pool-in-serverless.md +++ b/_archives/configure-cognito-user-pool-in-serverless.md @@ -6,9 +6,10 @@ lang: en description: We can define our Cognito User Pool using the Infrastructure as Code pattern by using CloudFormation in our serverless.yml. We are going to set the User Pool and App Client name based on the stage we are deploying to. We will also output the User Pool and App Client Id. ref: configure-cognito-user-pool-in-serverless comments_id: configure-cognito-user-pool-in-serverless/164 +redirect_from: /chapters/configure-cognito-user-pool-in-serverless.html --- -Now let's look into setting up Cognito User Pool through the `serverless.yml`. It should be very similar to the one we did by hand in the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter. +Now let's look into setting up Cognito User Pool through the `serverless.yml`. It should be very similar to the one we did by hand in the [Create a Cognito user pool]({% link _archives/create-a-cognito-user-pool.md %}) chapter. ### Create the Resource diff --git a/_chapters/configure-dynamodb-in-serverless.md b/_archives/configure-dynamodb-in-serverless.md similarity index 97% rename from _chapters/configure-dynamodb-in-serverless.md rename to _archives/configure-dynamodb-in-serverless.md index a9651b76d..8e5ecbcae 100644 --- a/_chapters/configure-dynamodb-in-serverless.md +++ b/_archives/configure-dynamodb-in-serverless.md @@ -4,12 +4,14 @@ title: Configure DynamoDB in serverless date: 2018-02-27 00:00:00 lang: en description: We can define our DynamoDB table using the Infrastructure as Code pattern by using CloudFormation in our serverless.yml. We are going to define the AttributeDefinitions, KeySchema, and ProvisionedThroughput. -redirect_from: /chapters/dynamodb-as-a-serverless-service.html ref: configure-dynamodb-in-serverless comments_id: configure-dynamodb-in-serverless/162 +redirect_from: + - /chapters/configure-dynamodb-in-serverless.html + - /chapters/dynamodb-as-a-serverless-service.html --- -For our Serverless Framework app, we had [previously created our DynamoDB table through the AWS console]({% link _chapters/create-a-dynamodb-table.md %}). This can be hard to do when you are creating multiple apps or environments. Ideally, we want to be able to do this programmatically. In this section we'll look at how to use [infrastructure as code]({% link _chapters/what-is-infrastructure-as-code.md %}) to do just that. +For our Serverless Framework app, we had [previously created our DynamoDB table through the AWS console]({% link _archives/create-a-dynamodb-table.md %}). This can be hard to do when you are creating multiple apps or environments. Ideally, we want to be able to do this programmatically. In this section we'll look at how to use [infrastructure as code]({% link _chapters/what-is-infrastructure-as-code.md %}) to do just that. ### Create the Resource diff --git a/_chapters/configure-multiple-aws-profiles.md b/_archives/configure-multiple-aws-profiles.md similarity index 98% rename from _chapters/configure-multiple-aws-profiles.md rename to _archives/configure-multiple-aws-profiles.md index 8e564e4b4..8f8e903a9 100644 --- a/_chapters/configure-multiple-aws-profiles.md +++ b/_archives/configure-multiple-aws-profiles.md @@ -4,6 +4,7 @@ title: Configure Multiple AWS Profiles description: To use multiple IAM credentials to deploy your serverless application you need to create a new AWS CLI profile. On local set the default AWS profile using the AWS_PROFILE bash variable. To deploy using your new profile use the "--aws-profile" option for the "serverless deploy" command. Alternatively, you can use the "profile:" setting in your serverless.yml. date: 2018-04-07 00:00:00 comments_id: configure-multiple-aws-profiles/21 +redirect_from: /chapters/configure-multiple-aws-profiles.html --- When we configured our AWS CLI in the [Configure the AWS CLI]({% link _chapters/configure-the-aws-cli.md %}) chapter, we used the `aws configure` command to set the IAM credentials of the AWS account we wanted to use to deploy our serverless application to. @@ -117,7 +118,7 @@ There are a couple of things happening here. - We also defined `custom.myProfile`, which contains the AWS profiles we want to use to deploy for each stage. Just as before we want to use the `prodAccount` profile if we are deploying to stage `prod` and the `devAccount` profile if we are deploying to stage `dev`. - Finally, we set the `provider.profile` to `${self:custom.myProfile.${self:custom.myStage}}`. This picks the value of our profile depending on the current stage defined in `custom.myStage`. -We used the concept of variables in Serverless Framework in this example. You can read more about this in the chapter on [Serverless Environment Variables]({% link _chapters/serverless-environment-variables.md %}). +We used the concept of variables in Serverless Framework in this example. You can read more about this in the chapter on [Serverless Environment Variables]({% link _archives/serverless-environment-variables.md %}). Now, when you deploy to production, Serverless Framework is going to use the `prodAccount` profile. And the resources will be provisioned inside `prodAccount` profile user's AWS account. diff --git a/_chapters/configure-s3-in-serverless.md b/_archives/configure-s3-in-serverless.md similarity index 92% rename from _chapters/configure-s3-in-serverless.md rename to _archives/configure-s3-in-serverless.md index 73c88d976..e90655323 100644 --- a/_chapters/configure-s3-in-serverless.md +++ b/_archives/configure-s3-in-serverless.md @@ -4,9 +4,11 @@ title: Configure S3 in serverless date: 2018-02-28 00:00:00 lang: en description: We can define our S3 Buckets using the Infrastructure as Code pattern by using CloudFormation in our serverless.yml. We are going to set the CORS policy and output the name of the bucket that's created. -redirect_from: /chapters/s3-as-a-serverless-service.html ref: configure-s3-in-serverless comments_id: configure-s3-in-serverless/163 +redirect_from: + - /chapters/configure-s3-in-serverless.html + - /chapters/s3-as-a-serverless-service.html --- Now that we have DynamoDB configured, let's look at how we can configure the S3 file uploads bucket through our `serverless.yml`. @@ -43,7 +45,7 @@ Outputs: Ref: AttachmentsBucket ``` -If you recall from the [Create an S3 bucket for file uploads]({% link _chapters/create-an-s3-bucket-for-file-uploads.md %}) chapter, we had created a bucket and configured the CORS policy for it. We needed to do this because we are going to be uploading directly from our frontend client. We configure the same policy here. +If you recall from the [Create an S3 bucket for file uploads]({% link _archives/create-an-s3-bucket-for-file-uploads.md %}) chapter, we had created a bucket and configured the CORS policy for it. We needed to do this because we are going to be uploading directly from our frontend client. We configure the same policy here. S3 buckets (unlike DynamoDB tables) are globally named, so it is not really possible for us to know what our bucket is going to be called beforehand. Hence, we let CloudFormation generate the name for us and we just add the `Outputs:` block to tell it to print it out so we can use it later. diff --git a/_chapters/connect-to-api-gateway-with-iam-auth.md b/_archives/connect-to-api-gateway-with-iam-auth.md similarity index 98% rename from _chapters/connect-to-api-gateway-with-iam-auth.md rename to _archives/connect-to-api-gateway-with-iam-auth.md index b358757aa..b37b50b43 100644 --- a/_chapters/connect-to-api-gateway-with-iam-auth.md +++ b/_archives/connect-to-api-gateway-with-iam-auth.md @@ -5,6 +5,7 @@ description: For our React.js app to make requests to a serverless backend API s date: 2018-04-11 00:00:00 comments_id: 113 comments_id: comments-signup-with-aws-cognito/113 +redirect_from: /chapters/connect-to-api-gateway-with-iam-auth.html --- Connecting to an API Gateway endpoint secured using AWS IAM can be challenging. You need to sign your requests using [Signature Version 4](http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html). You can use: diff --git a/_chapters/create-a-cloudfront-distribution.md b/_archives/create-a-cloudfront-distribution.md similarity index 98% rename from _chapters/create-a-cloudfront-distribution.md rename to _archives/create-a-cloudfront-distribution.md index 47ab572c7..aad08dcec 100644 --- a/_chapters/create-a-cloudfront-distribution.md +++ b/_archives/create-a-cloudfront-distribution.md @@ -6,6 +6,7 @@ lang: en description: To server out our React.js app hosted on Amazon S3 through a CDN we are going to use CloudFront. We will create a CloudFront Distribution and point it to our S3 Bucket. We are also going to enable Gzip compression using the “Compress Objects Automatically” setting in the AWS console. And to ensure that our React.js app responds with the right HTTP headers, we will create a Custom Error Response. comments_id: create-a-cloudfront-distribution/104 ref: create-a-cloudfront-distribution +redirect_from: /chapters/create-a-cloudfront-distribution.html --- Now that we have our app up and running on S3, let's serve it out globally through CloudFront. To do this we need to create an AWS CloudFront Distribution. diff --git a/_chapters/create-a-cognito-identity-pool.md b/_archives/create-a-cognito-identity-pool.md similarity index 95% rename from _chapters/create-a-cognito-identity-pool.md rename to _archives/create-a-cognito-identity-pool.md index a7515bcda..62c4d064d 100644 --- a/_chapters/create-a-cognito-identity-pool.md +++ b/_archives/create-a-cognito-identity-pool.md @@ -6,6 +6,7 @@ lang: en ref: create-a-cognito-identity-pool description: Amazon Cognito Federated Identities helps us secure our AWS resources. We can use the Cognito User Pool as an identity provider for our serverless backend. To allow users to be able to upload files to our S3 bucket and connect to API Gateway we need to create an Identity Pool. We will assign it an IAM Policy with the name of our S3 bucket and prefix our files with the cognito-identity.amazonaws.com:sub. And we’ll add our API Gateway endpoint as a resource as well. comments_id: create-a-cognito-identity-pool/135 +redirect_from: /chapters/create-a-cognito-identity-pool.html --- Now that we have deployed our backend API; we almost have all the pieces we need for our backend. We have the User Pool that is going to store all of our users and help sign in and sign them up. We also have an S3 bucket that we will use to help our users upload files as attachments for their notes. The final piece that ties all these services together in a secure way is called Amazon Cognito Federated Identities. @@ -30,7 +31,7 @@ Enter an **Identity pool name**. If you have any existing Identity Pools, you'll ![Fill Cognito Identity Pool Info Screenshot](/assets/cognito-identity-pool/fill-identity-pool-info.png) -Select **Authentication providers**. Under **Cognito** tab, enter **User Pool ID** and **App Client ID** of the User Pool created in the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter. Select **Create Pool**. +Select **Authentication providers**. Under **Cognito** tab, enter **User Pool ID** and **App Client ID** of the User Pool created in the [Create a Cognito user pool]({% link _archives/create-a-cognito-user-pool.md %}) chapter. Select **Create Pool**. ![Fill Authentication Provider Info Screenshot](/assets/cognito-identity-pool/fill-authentication-provider-info.png) @@ -46,7 +47,7 @@ It will warn you to read the documentation. Select **Ok** to edit. ![Select Confirm Edit Policy Screenshot](/assets/cognito-identity-pool/select-confirm-edit-policy.png) -{%change%} Add the following policy into the editor. Replace `YOUR_S3_UPLOADS_BUCKET_NAME` with the **bucket name** from the [Create an S3 bucket for file uploads]({% link _chapters/create-an-s3-bucket-for-file-uploads.md %}) chapter. And replace the `YOUR_API_GATEWAY_REGION` and `YOUR_API_GATEWAY_ID` with the ones that you got back in the [Deploy the APIs]({% link _chapters/deploy-the-apis.md %}) chapter. +{%change%} Add the following policy into the editor. Replace `YOUR_S3_UPLOADS_BUCKET_NAME` with the **bucket name** from the [Create an S3 bucket for file uploads]({% link _archives/create-an-s3-bucket-for-file-uploads.md %}) chapter. And replace the `YOUR_API_GATEWAY_REGION` and `YOUR_API_GATEWAY_ID` with the ones that you got back in the [Deploy the APIs]({% link _archives/deploy-the-apis.md %}) chapter. In our case `YOUR_S3_UPLOADS_BUCKET_NAME` is `notes-app-uploads`, `YOUR_API_GATEWAY_ID` is `ly55wbovq4`, and `YOUR_API_GATEWAY_REGION` is `us-east-1`. @@ -87,7 +88,7 @@ In our case `YOUR_S3_UPLOADS_BUCKET_NAME` is `notes-app-uploads`, `YOUR_API_GATE } ``` -Once a user has been authenticated with our User Pool and verified with our Identity Pool, he/she is assigned this [IAM role]({% link _chapters/what-is-iam.md %}). This role limits what our user has access to in our AWS account. +Once a user has been authenticated with our User Pool and verified with our Identity Pool, he/she is assigned this [IAM role]({% link _archives/what-is-iam.md %}). This role limits what our user has access to in our AWS account. A quick note on the block that relates to the S3 Bucket. In the above policy we are granting our logged in users access to the path `private/${cognito-identity.amazonaws.com:sub}/`. Where `cognito-identity.amazonaws.com:sub` is the authenticated user's federated identity ID (their user id). So a user has access to only their folder within the bucket. This is how we are securing the uploads for each user. diff --git a/_chapters/create-a-cognito-test-user.md b/_archives/create-a-cognito-test-user.md similarity index 96% rename from _chapters/create-a-cognito-test-user.md rename to _archives/create-a-cognito-test-user.md index 869e22d4b..f37f62118 100644 --- a/_chapters/create-a-cognito-test-user.md +++ b/_archives/create-a-cognito-test-user.md @@ -6,6 +6,7 @@ lang: en ref: create-a-cognito-test-user description: To test using the Cognito User Pool as an authorizer for our serverless API backend, we are going to create a test user. We can create a user from the AWS CLI using the aws cognito-idp sign-up and admin-confirm-sign-up command. comments_id: create-a-cognito-test-user/126 +redirect_from: /chapters/create-a-cognito-test-user.html --- In this chapter, we are going to create a test user for our Cognito User Pool. We are going to need this user to test the authentication portion of our app later. diff --git a/_chapters/create-a-cognito-user-pool.md b/_archives/create-a-cognito-user-pool.md similarity index 98% rename from _chapters/create-a-cognito-user-pool.md rename to _archives/create-a-cognito-user-pool.md index a57e54f0c..afe3d65a0 100644 --- a/_chapters/create-a-cognito-user-pool.md +++ b/_archives/create-a-cognito-user-pool.md @@ -6,6 +6,7 @@ lang: en ref: create-a-cognito-user-pool description: Amazon Cognito User Pool handles sign-up and sign-in functionality for web and mobile apps. We are going to create a Cognito User Pool to store and manage the users for our serverless app. We'll use the email address as username option since we want our users to login with their email. We are also going to set up our app as an App Client for our Cognito User Pool. comments_id: create-a-cognito-user-pool/148 +redirect_from: /chapters/create-a-cognito-user-pool.html --- Our notes app needs to handle user accounts and authentication in a secure and reliable way. To do this we are going to use [Amazon Cognito](https://aws.amazon.com/cognito/). diff --git a/_chapters/create-a-dynamodb-table.md b/_archives/create-a-dynamodb-table.md similarity index 98% rename from _chapters/create-a-dynamodb-table.md rename to _archives/create-a-dynamodb-table.md index 2e8a24ce8..27e45597a 100644 --- a/_chapters/create-a-dynamodb-table.md +++ b/_archives/create-a-dynamodb-table.md @@ -6,6 +6,7 @@ lang: en ref: create-a-dynamodb-table description: Amazon DynamoDB is a fully managed NoSQL database that we are going to use to power our serverless API backend. DynamoDB stores data in tables and each table has a primary key that cannot be changed once set. We are also going to provision the throughput capacity by setting reads and writes for our DynamoDB table. comments_id: create-a-dynamodb-table/139 +redirect_from: /chapters/create-a-dynamodb-table.html --- We are going to build a REST API for our notes app. It's a simple [CRUD (create, read, update, and delete)](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) API. Meaning that we'll be performing these operations on our database. We also want our users to be able to upload files as attachments to their notes. @@ -66,7 +67,7 @@ The `notes` table has now been created. If you find yourself stuck with the **Ta ![Select DynamoDB Service screenshot](/assets/dynamodb/dynamodb-table-created.png) -It is also a good idea to set up backups for your DynamoDB table, especially if you are planning to use it in production. We cover this in an extra-credit chapter, [Backups in DynamoDB]({% link _chapters/backups-in-dynamodb.md %}). +It is also a good idea to set up backups for your DynamoDB table, especially if you are planning to use it in production. We cover this in an extra-credit chapter, [Backups in DynamoDB]({% link _archives/backups-in-dynamodb.md %}). Next let's look at how we are going to store the files that our users upload. diff --git a/_chapters/create-a-netlify-build-script.md b/_archives/create-a-netlify-build-script.md similarity index 92% rename from _chapters/create-a-netlify-build-script.md rename to _archives/create-a-netlify-build-script.md index eb9bacc4e..eb70738cb 100644 --- a/_chapters/create-a-netlify-build-script.md +++ b/_archives/create-a-netlify-build-script.md @@ -5,11 +5,13 @@ date: 2018-03-26 00:00:00 lang: en description: To configure our Create React App with Netlify, we need to add a build script to our project root. To make sure that we return a HTTP status code of 200 for our React Router routes we will be adding a redirects rule. ref: create-a-netlify-build-script -redirect_from: /chapters/create-a-build-script.html comments_id: create-a-build-script/189 +redirect_from: + - /chapters/create-a-netlify-build-script.html + - /chapters/create-a-build-script.html --- -To automate our React.js deployments with [Netlify](https://www.netlify.com) we just need to set up a build script. If you recall from the [previous chapter]({% link _chapters/manage-environments-in-create-react-app.md %}), we had configured our app to use the `REACT_APP_STAGE` build environment variable. We are going to create a build script to tell Netlify to set this variable up for the different deployment cases. +To automate our React.js deployments with [Netlify](https://www.netlify.com) we just need to set up a build script. If you recall from the [previous chapter]({% link _archives/manage-environments-in-create-react-app.md %}), we had configured our app to use the `REACT_APP_STAGE` build environment variable. We are going to create a build script to tell Netlify to set this variable up for the different deployment cases. ### Add the Netlify Build Script @@ -48,7 +50,7 @@ The build script is configured based on contexts. There is a default one right u 2. The `publish` option points to where our build is generated. In the case of Create React App it is the `build` directory in our project root. -3. The `command` option is the build command that Netlify will use. If you recall the [Manage environments in Create React App]({% link _chapters/manage-environments-in-create-react-app.md %}) chapter, this will seem familiar. In the default context the command is `REACT_APP_STAGE=dev npm run build`. +3. The `command` option is the build command that Netlify will use. If you recall the [Manage environments in Create React App]({% link _archives/manage-environments-in-create-react-app.md %}) chapter, this will seem familiar. In the default context the command is `REACT_APP_STAGE=dev npm run build`. The production context labelled, `context.production` is the only one where we set the `REACT_APP_STAGE` variable to `prod`. This is when we push to `master`. The `branch-deploy` is what we will be using when we push to any other non-production branch. The `deploy-preview` is for pull requests. diff --git a/_chapters/create-an-s3-bucket-for-file-uploads.md b/_archives/create-an-s3-bucket-for-file-uploads.md similarity index 94% rename from _chapters/create-an-s3-bucket-for-file-uploads.md rename to _archives/create-an-s3-bucket-for-file-uploads.md index dec63b009..1ebcaddbe 100644 --- a/_chapters/create-an-s3-bucket-for-file-uploads.md +++ b/_archives/create-an-s3-bucket-for-file-uploads.md @@ -5,11 +5,13 @@ date: 2016-12-27 00:00:00 lang: en ref: create-an-s3-bucket-for-file-uploads description: To allow users to upload files to our serverless app we are going to use Amazon S3 (Simple Storage Service). S3 allows you to store files and organize them into buckets. -redirect_from: /chapters/create-a-s3-bucket-for-file-uploads.html comments_id: create-an-s3-bucket-for-file-uploads/150 +redirect_from: + - /chapters/create-an-s3-bucket-for-file-uploads.html + - /chapters/create-a-s3-bucket-for-file-uploads.html --- -Now that we have [our database table]({% link _chapters/create-a-dynamodb-table.md %}) ready; let's get things set up for handling file uploads. We need to handle file uploads because each note can have an uploaded file as an attachment. +Now that we have [our database table]({% link _archives/create-a-dynamodb-table.md %}) ready; let's get things set up for handling file uploads. We need to handle file uploads because each note can have an uploaded file as an attachment. [Amazon S3](https://aws.amazon.com/s3/) (Simple Storage Service) provides storage service through web services interfaces like REST. You can store any object in S3 including images, videos, files, etc. Objects are organized into buckets, and identified within each bucket by a unique, user-assigned key. diff --git a/_chapters/create-an-s3-bucket.md b/_archives/create-an-s3-bucket.md similarity index 97% rename from _chapters/create-an-s3-bucket.md rename to _archives/create-an-s3-bucket.md index e508412e8..48c645c1e 100644 --- a/_chapters/create-an-s3-bucket.md +++ b/_archives/create-an-s3-bucket.md @@ -3,10 +3,12 @@ layout: post title: Create an S3 Bucket date: 2017-02-06 00:00:00 lang: en -redirect_from: /chapters/create-a-s3-bucket.html description: We are going to use S3 to host our React.js app on AWS. We first need to configure our S3 bucket with the correct Bucket Policy and enable Static Web Hosting through the AWS console before we can upload our app. comments_id: create-an-s3-bucket/48 ref: create-an-s3-bucket +redirect_from: + - /chapters/create-an-s3-bucket.html + - /chapters/create-a-s3-bucket.html --- To be able to host our note taking app, we need to upload the assets that are going to be served out statically on S3. S3 has a concept of buckets (or folders) to separate different types of files. @@ -47,7 +49,7 @@ Buckets by default are not publicly accessible, so we need to change the S3 Buck {%change%} Add the following bucket policy into the editor. Where `notes-app-client` is the name of our S3 bucket. Make sure to use the name of your bucket here. -``` json +```json { "Version":"2012-10-17", "Statement":[{ diff --git a/_chapters/creating-a-ci-cd-pipeline-for-react.md b/_archives/creating-a-ci-cd-pipeline-for-react.md similarity index 84% rename from _chapters/creating-a-ci-cd-pipeline-for-react.md rename to _archives/creating-a-ci-cd-pipeline-for-react.md index abb451f60..2980707e7 100644 --- a/_chapters/creating-a-ci-cd-pipeline-for-react.md +++ b/_archives/creating-a-ci-cd-pipeline-for-react.md @@ -5,11 +5,13 @@ date: 2020-11-04 00:00:00 lang: en description: In this chapter we are going to look at how to create a CI/CD pipeline for our React app. We'll be using a service called Netlify for this. And we'll be using a branch based Git workflow. ref: creating-a-ci-cd-pipeline-for-react -redirect_from: /chapters/automating-react-deployments.html comments_id: creating-a-ci-cd-pipeline-for-react/188 +redirect_from: + - /chapters/creating-a-ci-cd-pipeline-for-react.html + - /chapters/automating-react-deployments.html --- -In the last couple of chapters, we [set our React.js app up in Netlify]({% link _chapters/setting-up-your-project-on-netlify.md %}) and [added a custom domain to it]({% link _chapters/custom-domain-in-netlify.md %}). In this chapter we'll look at how to use Netlify to create a CI/CD pipeline for our React app. +In the last couple of chapters, we [set our React.js app up in Netlify]({% link _archives/setting-up-your-project-on-netlify.md %}) and [added a custom domain to it]({% link _archives/custom-domain-in-netlify.md %}). In this chapter we'll look at how to use Netlify to create a CI/CD pipeline for our React app. Here's what the CI/CD pipeline for our React app will look like. diff --git a/_chapters/creating-feature-environments.md b/_archives/creating-feature-environments.md similarity index 99% rename from _chapters/creating-feature-environments.md rename to _archives/creating-feature-environments.md index 317707faa..3e3c6048e 100644 --- a/_chapters/creating-feature-environments.md +++ b/_archives/creating-feature-environments.md @@ -4,6 +4,7 @@ title: Creating Feature Environments description: In this chapter we look at how to create feature environments for your serverless app. We'll go over the process of creating a new feature branch in Git and adding a new Serverless service using Seed. date: 2019-10-02 00:00:00 comments_id: creating-feature-environments/1317 +redirect_from: /chapters/creating-feature-environments.html --- Over the last couple of chapters we looked at how to work on Lambda and API Gateway locally. However, besides Lambda and API Gateway, your project will have other AWS services. To run your code locally, you have to simulate all the AWS services. Similar to [serverless-offline](https://www.github.com/dherault/serverless-offline), there are plugins like [serverless-dynamodb-local](https://www.github.com/99xt/serverless-dynamodb-local) and [serverless-offline-sns](https://github.com/mj1618/serverless-offline-sns) that can simulate DynamoDB and SNS. However, mocking only takes you so far since they do not simulate IAM permissions and they are not always up to date with the services' latest changes. You want to test your code with the real resources. @@ -140,7 +141,7 @@ Select the **dev** stage, since we want the stage to be deployed into the **Deve ![Select Enable Auto-Deploy](/assets/best-practices/creating-feature-environments/select-enable-auto-deploy.png) -Click **Pipeline** to head back. +Click **Pipeline** to go. ![Head back to pipeline](/assets/best-practices/creating-feature-environments/head-back-to-pipeline.png) diff --git a/_chapters/creating-pull-request-environments.md b/_archives/creating-pull-request-environments.md similarity index 98% rename from _chapters/creating-pull-request-environments.md rename to _archives/creating-pull-request-environments.md index ea2f8f779..1fb0f902c 100644 --- a/_chapters/creating-pull-request-environments.md +++ b/_archives/creating-pull-request-environments.md @@ -4,6 +4,7 @@ title: Creating Pull Request Environments description: In this chapter we'll look at the workflow for creating new pull request based environments for your serverless app using Seed. date: 2019-10-02 00:00:00 comments_id: creating-pull-request-environments/1318 +redirect_from: /chapters/creating-pull-request-environments.html --- Now that we are done working on our new feature, we would like our team lead to review our work before promoting it to production. To do that we are going to create a pull request and Seed will automatically create an ephemeral environment for it. diff --git a/_chapters/cross-stack-references-in-serverless.md b/_archives/cross-stack-references-in-serverless.md similarity index 96% rename from _chapters/cross-stack-references-in-serverless.md rename to _archives/cross-stack-references-in-serverless.md index bf0120fdc..c93e868d5 100644 --- a/_chapters/cross-stack-references-in-serverless.md +++ b/_archives/cross-stack-references-in-serverless.md @@ -5,9 +5,10 @@ description: AWS CloudFormation allows us to link multiple serverless services u date: 2018-04-02 13:00:00 ref: cross-stack-references-in-serverless comments_id: cross-stack-references-in-serverless/405 +redirect_from: /chapters/cross-stack-references-in-serverless.html --- -In the previous chapter we looked at [some of the most common patterns for organizing your serverless applications]({% link _chapters/organizing-serverless-projects.md %}). Now let's look at how to work with multiple services in your Serverless application. +In the previous chapter we looked at [some of the most common patterns for organizing your serverless applications]({% link _archives/organizing-serverless-projects.md %}). Now let's look at how to work with multiple services in your Serverless application. You might recall that a Serverless Framework service is where a single `serverless.yml` is used to define the project. And the `serverless.yml` file is converted into a [CloudFormation template](https://aws.amazon.com/cloudformation/aws-cloudformation-templates/) using Serverless Framework. This means that in the case of multiple services you might need to reference a resource that is available in a different service. diff --git a/_chapters/custom-domain-in-netlify.md b/_archives/custom-domain-in-netlify.md similarity index 93% rename from _chapters/custom-domain-in-netlify.md rename to _archives/custom-domain-in-netlify.md index dbdbe1cc7..a0fb05fd1 100644 --- a/_chapters/custom-domain-in-netlify.md +++ b/_archives/custom-domain-in-netlify.md @@ -6,9 +6,10 @@ lang: en description: To configure your React app with custom domains on Netlify and AWS, you need to point the Route 53 DNS to Netlify. Create a new Record set, add an A Record, and a CNAME for your new Netlify project. ref: custom-domains-in-netlify comments_id: custom-domains-in-netlify/191 +redirect_from: /chapters/custom-domain-in-netlify.html --- -Now that we [have our React app hosted on Netlify]({% link _chapters/setting-up-your-project-on-netlify.md %}), let's configure a custom domain. +Now that we [have our React app hosted on Netlify]({% link _archives/setting-up-your-project-on-netlify.md %}), let's configure a custom domain. Before we get started, make sure to follow this chapter to [purchase a domain on Amazon Route 53]({% link _chapters/purchase-a-domain-with-route-53.md %}). @@ -56,7 +57,7 @@ This will show you the instructions for setting up your domain through Route 53. ### DNS Settings in Route 53 -To do this we need to head back to the [AWS Console](https://console.aws.amazon.com/). and search for Route 53 as the service. +To do this we need to go to the [AWS Console](https://console.aws.amazon.com/). and search for Route 53 as the service. ![Select Route 53 service screenshot](/assets/select-route-53-service.png) diff --git a/_chapters/customize-the-serverless-iam-policy.md b/_archives/customize-the-serverless-iam-policy.md similarity index 99% rename from _chapters/customize-the-serverless-iam-policy.md rename to _archives/customize-the-serverless-iam-policy.md index c9afca9b1..16a8ffd03 100644 --- a/_chapters/customize-the-serverless-iam-policy.md +++ b/_archives/customize-the-serverless-iam-policy.md @@ -4,6 +4,7 @@ title: Customize the serverless IAM Policy description: Serverless Framework deploys using the policy attached to the IAM credentials in your AWS CLI profile. To customize the IAM Policy used, access can be restricted to the services that Serverless Framework needs, and to the project that is being deployed. date: 2018-04-08 00:00:00 comments_id: customize-the-serverless-iam-policy/18 +redirect_from: /chapters/customize-the-serverless-iam-policy.html --- Serverless Framework deploys using the policy attached to the IAM credentials in your AWS CLI profile. Back in the [Create an IAM User]({% link _chapters/create-an-iam-user.md %}) chapter we created a user that the Serverless Framework will use to deploy our project. This user was assigned **AdministratorAccess**. This means that Serverless Framework and your project has complete access to your AWS account. This is fine in trusted environments but if you are working as a part of a team you might want to fine-tune the level of access based on who is using your project. diff --git a/_chapters/debugging-serverless-api-issues.md b/_archives/debugging-serverless-api-issues.md similarity index 97% rename from _chapters/debugging-serverless-api-issues.md rename to _archives/debugging-serverless-api-issues.md index 7eb839bb9..9c5186453 100644 --- a/_chapters/debugging-serverless-api-issues.md +++ b/_archives/debugging-serverless-api-issues.md @@ -4,6 +4,7 @@ title: Debugging Serverless API Issues description: Debugging serverless APIs can be tricky because there isn’t enough visibility on all the steps a request goes through. A very common issue is an invalid or missing IAM Role while using aws_iam as an authorizer for API Gateway and Lambda. To fix this use the Policy Simulator to ensure that your IAM Role has access to API Gateway. date: 2018-04-04 00:00:00 comments_id: debugging-serverless-api-issues/143 +redirect_from: /chapters/debugging-serverless-api-issues.html --- In this chapter we are going to take a brief look at some common API Gateway and Lambda issues we come across and how to debug them. @@ -17,7 +18,7 @@ When a request is made to your serverless API, it starts by hitting API Gateway - [Lambda Function Error](#lambda-function-error) - [Lambda Function Timeout](#lambda-function-timeout) -This chapter assumes you have turned on CloudWatch logging for API Gateway and that you know how to read both the API Gateway and Lambda logs. If you have not done so, start by taking a look at the chapter on [API Gateway and Lambda Logs]({% link _chapters/api-gateway-and-lambda-logs.md %}). +This chapter assumes you have turned on CloudWatch logging for API Gateway and that you know how to read both the API Gateway and Lambda logs. If you have not done so, start by taking a look at the chapter on [API Gateway and Lambda Logs]({% link _archives/api-gateway-and-lambda-logs.md %}). ### Invalid API Endpoint @@ -47,7 +48,7 @@ assigned to the Cognito Identity Pool has not been granted the **execute-api:Inv This is a tricky issue to debug because the request still has not reached API Gateway, and hence the error is not logged in the API Gateway CloudWatch logs. But we can perform a check to ensure that our Cognito Identity Pool users have the required permissions, using the [IAM policy Simulator](https://policysim.aws.amazon.com). -Before we can use the simulator we first need to find out the name of the IAM role that we are using to connect to API Gateway. We had created this role back in the [Create a Cognito identity pool]({% link _chapters/create-a-cognito-identity-pool.md %}) chapter. +Before we can use the simulator we first need to find out the name of the IAM role that we are using to connect to API Gateway. We had created this role back in the [Create a Cognito identity pool]({% link _archives/create-a-cognito-identity-pool.md %}) chapter. Select **Cognito** from your [AWS Console](https://console.aws.amazon.com). @@ -85,7 +86,7 @@ Select **API Gateway** as the service and select the **Invoke** action. ![Select IAM Service Simulator Action](/assets/debugging/select-iam-policy-simulator-action.png) -Expand the service and enter the API Gateway endpoint ARN, then select **Run Simulation**. The format here is the same one we used back in the [Create a Cognito identity pool]({% link _chapters/create-a-cognito-identity-pool.md %}) chapter; `arn:aws:execute-api:YOUR_API_GATEWAY_REGION:*:YOUR_API_GATEWAY_ID/*`. In our case this looks like `arn:aws:execute-api:us-east-1:*:ly55wbovq4/*`. +Expand the service and enter the API Gateway endpoint ARN, then select **Run Simulation**. The format here is the same one we used back in the [Create a Cognito identity pool]({% link _archives/create-a-cognito-identity-pool.md %}) chapter; `arn:aws:execute-api:YOUR_API_GATEWAY_REGION:*:YOUR_API_GATEWAY_ID/*`. In our case this looks like `arn:aws:execute-api:us-east-1:*:ly55wbovq4/*`. ![Enter API Gateway Endpoint ARN](/assets/debugging/enter-api-gateway-endpoint-arn.png) diff --git a/_chapters/deploy-a-serverless-app-with-dependencies.md b/_archives/deploy-a-serverless-app-with-dependencies.md similarity index 96% rename from _chapters/deploy-a-serverless-app-with-dependencies.md rename to _archives/deploy-a-serverless-app-with-dependencies.md index 2ddc21b13..26095ba42 100644 --- a/_chapters/deploy-a-serverless-app-with-dependencies.md +++ b/_archives/deploy-a-serverless-app-with-dependencies.md @@ -2,9 +2,11 @@ layout: post title: Deploy a Serverless App with Dependencies description: In this chapter we go over how to locally deploy a serverless app with multiple interdependent services. So you'll need to ensure that you deploy the service that is exporting the reference before deploying the one that imports it. You'll only need to do this for the first time. -redirect_from: /chapters/deploying-multiple-services-in-serverless.html date: 2019-09-29 00:00:00 comments_id: deploying-multiple-services-in-serverless/410 +redirect_from: + - /chapters/deploy-a-serverless-app-with-dependencies.html + - /chapters/deploying-multiple-services-in-serverless.html --- So now that we have a couple of downstream services that are referencing a resource deployed in an upstream service; let's look at how this dependency affects the way we deploy our app. diff --git a/_chapters/deploy-the-api-services-repo.md b/_archives/deploy-the-api-services-repo.md similarity index 98% rename from _chapters/deploy-the-api-services-repo.md rename to _archives/deploy-the-api-services-repo.md index 2c902bfe6..b3bfd8b24 100644 --- a/_chapters/deploy-the-api-services-repo.md +++ b/_archives/deploy-the-api-services-repo.md @@ -4,6 +4,7 @@ title: Deploy the API Services Repo description: In this chapter we'll deploy our demo API services GitHub repo of our serverless app to multiple AWS environments. We'll be using Seed to manage our deployments and environments. date: 2019-10-08 00:00:00 comments_id: deploy-the-api-services-repo/1319 +redirect_from: /chapters/deploy-the-api-services-repo.html --- Just as the previous chapter we'll add the API repo on Seed and deploy it to our environments. diff --git a/_chapters/deploy-the-apis.md b/_archives/deploy-the-apis.md similarity index 97% rename from _chapters/deploy-the-apis.md rename to _archives/deploy-the-apis.md index f0e7a56b8..3dd8aba0a 100644 --- a/_chapters/deploy-the-apis.md +++ b/_archives/deploy-the-apis.md @@ -6,6 +6,7 @@ lang: en ref: deploy-the-apis description: Use the “serverless deploy” command to deploy to AWS Lambda and API Gateway using the Serverless Framework. Running this command will display the list of deployed API endpoints and the AWS region it was deployed to. And we can run the "serverless deploy function" command when we want to update an individual Lambda function. comments_id: deploy-the-apis/121 +redirect_from: /chapters/deploy-the-apis.html --- So far we've been working on our Lambda functions locally. In this chapter we are going to deploy them. @@ -22,7 +23,7 @@ If you have multiple profiles for your AWS SDK credentials, you will need to exp $ serverless deploy --aws-profile myProfile ``` -Where `myProfile` is the name of the AWS profile you want to use. If you need more info on how to work with AWS profiles in serverless, refer to our [Configure multiple AWS profiles]({% link _chapters/configure-multiple-aws-profiles.md %}) chapter. +Where `myProfile` is the name of the AWS profile you want to use. If you need more info on how to work with AWS profiles in serverless, refer to our [Configure multiple AWS profiles]({% link _archives/configure-multiple-aws-profiles.md %}) chapter. Near the bottom of the output for this command, you will find the **Service Information**. diff --git a/_chapters/deploy-the-resources-repo.md b/_archives/deploy-the-resources-repo.md similarity index 97% rename from _chapters/deploy-the-resources-repo.md rename to _archives/deploy-the-resources-repo.md index 0090b8c3c..1b62ac92d 100644 --- a/_chapters/deploy-the-resources-repo.md +++ b/_archives/deploy-the-resources-repo.md @@ -4,6 +4,7 @@ title: Deploy the Resources Repo description: In this chapter we'll deploy our demo resources GitHub repo of our serverless app to multiple AWS environments. We'll be using Seed to manage our deployments and environments. date: 2019-10-08 00:00:00 comments_id: deploy-the-resources-repo/1320 +redirect_from: /chapters/deploy-the-resources-repo.html --- First, add the resources repo on Seed. If you haven't yet, you can create a free account [here](https://console.seed.run/signup). @@ -48,7 +49,7 @@ After the service is successfully deployed. Click **Promote** to deploy this to ![Select Promote in dev stage](/assets/best-practices/deploy-resources-repo-to-seed/select-promote-in-dev-stage.png) -You will see a list of changes in resources. Since this is the first time we are deploying to the `prod` stage, the change list shows all the resources that will be created. We'll take a look at this in detail later in the [Promoting to production]({% link _chapters/promoting-to-production.md %}) chapter. +You will see a list of changes in resources. Since this is the first time we are deploying to the `prod` stage, the change list shows all the resources that will be created. We'll take a look at this in detail later in the [Promoting to production]({% link _archives/promoting-to-production.md %}) chapter. Click **Promote to Production**. diff --git a/_chapters/deploy-to-s3.md b/_archives/deploy-to-s3.md similarity index 93% rename from _chapters/deploy-to-s3.md rename to _archives/deploy-to-s3.md index 0d0bfefe0..c350dc417 100644 --- a/_chapters/deploy-to-s3.md +++ b/_archives/deploy-to-s3.md @@ -6,6 +6,7 @@ lang: en description: To use our React.js app in production we are going to use Create React App’s build command to create a production build of our app. And to upload our React.js app to an S3 Bucket on AWS, we are going to use the AWS CLI s3 sync command. comments_id: deploy-to-s3/134 ref: deploy-to-s3 +redirect_from: /chapters/deploy-to-s3.html --- Now that our S3 Bucket is created we are ready to upload the assets of our app. @@ -22,7 +23,7 @@ This packages all of our assets and places them in the `build/` directory. ### Upload to S3 -Now to deploy simply run the following command; where `YOUR_S3_DEPLOY_BUCKET_NAME` is the name of the S3 Bucket we created in the [Create an S3 bucket]({% link _chapters/create-an-s3-bucket.md %}) chapter. +Now to deploy simply run the following command; where `YOUR_S3_DEPLOY_BUCKET_NAME` is the name of the S3 Bucket we created in the [Create an S3 bucket]({% link _archives/create-an-s3-bucket.md %}) chapter. ``` bash $ aws s3 sync build/ s3://YOUR_S3_DEPLOY_BUCKET_NAME diff --git a/_chapters/deploy-updates.md b/_archives/deploy-updates.md similarity index 97% rename from _chapters/deploy-updates.md rename to _archives/deploy-updates.md index 7e075ba20..80132e31e 100644 --- a/_chapters/deploy-updates.md +++ b/_archives/deploy-updates.md @@ -4,9 +4,11 @@ title: Deploy Updates date: 2017-02-12 00:00:00 lang: en description: To be able to deploy updates to our React.js app hosted on S3 and CloudFront, we need to uploads our app to S3 and invalidate the CloudFront cache. We can do this using the “aws cloudfront create-invalidation” command in our AWS CLI. To automate these steps by running “npx sst deploy”, we will add these commands to predeploy, deploy, and postdeploy scripts in our package.json. -redirect_from: /chapters/deploy-again.html comments_id: deploy-updates/16 ref: deploy-updates +redirect_from: + - /chapters/deploy-updates.html + - /chapters/deploy-again.html --- Now let's look at how we make changes and update our app. The process is very similar to how we deployed our code to S3 but with a few changes. Here is what it looks like. @@ -31,7 +33,7 @@ Now that our app is built and ready in the `build/` directory, let's deploy to S ### Upload to S3 -Run the following from our working directory to upload our app to our main S3 Bucket. Make sure to replace `YOUR_S3_DEPLOY_BUCKET_NAME` with the S3 Bucket we created in the [Create an S3 bucket]({% link _chapters/create-an-s3-bucket.md %}) chapter. +Run the following from our working directory to upload our app to our main S3 Bucket. Make sure to replace `YOUR_S3_DEPLOY_BUCKET_NAME` with the S3 Bucket we created in the [Create an S3 bucket]({% link _archives/create-an-s3-bucket.md %}) chapter. ```bash $ aws s3 sync build/ s3://YOUR_S3_DEPLOY_BUCKET_NAME --delete diff --git a/_chapters/deploy-your-hello-world-api.md b/_archives/deploy-your-hello-world-api.md similarity index 94% rename from _chapters/deploy-your-hello-world-api.md rename to _archives/deploy-your-hello-world-api.md index a039f24b6..b842987e0 100644 --- a/_chapters/deploy-your-hello-world-api.md +++ b/_archives/deploy-your-hello-world-api.md @@ -6,9 +6,10 @@ lang: en ref: deploy-your-first-serverless-api description: In this chapter we'll be deploying our first Hello World serverless API. We'll be using the `serverless deploy` command to deploy it to AWS. comments_id: deploy-your-hello-world-api/2173 +redirect_from: /chapters/deploy-your-hello-world-api.html --- -So far we've [created our Serverless Framework]({% link _chapters/setup-the-serverless-framework.md %}) app. Now we are going to deploy it to AWS. Make sure you've [configured your AWS account]({% link _chapters/create-an-aws-account.md %}) and [AWS CLI]({% link _chapters/configure-the-aws-cli.md %}). +So far we've [created our Serverless Framework]({% link _archives/setup-the-serverless-framework.md %}) app. Now we are going to deploy it to AWS. Make sure you've [configured your AWS account]({% link _chapters/create-an-aws-account.md %}) and [AWS CLI]({% link _chapters/configure-the-aws-cli.md %}). A big advantage with serverless is that there isn't any infrastructure or servers to provision. You can simply deploy your app directly and it's ready to serve (millions of) users right away. diff --git a/_chapters/deploy-your-serverless-infrastructure.md b/_archives/deploy-your-serverless-infrastructure.md similarity index 88% rename from _chapters/deploy-your-serverless-infrastructure.md rename to _archives/deploy-your-serverless-infrastructure.md index f097fdfc8..026d81f68 100644 --- a/_chapters/deploy-your-serverless-infrastructure.md +++ b/_archives/deploy-your-serverless-infrastructure.md @@ -7,6 +7,7 @@ description: In this chapter we'll be deploying our entire serverless infrastruc code: backend_full ref: deploy-your-serverless-infrastructure comments_id: deploy-your-serverless-infrastructure/167 +redirect_from: /chapters/deploy-your-serverless-infrastructure.html --- Now that we have all our resources configured, let's go ahead and deploy our entire infrastructure. @@ -72,6 +73,6 @@ You can also deploy your app to production by running. $ serverless deploy --stage prod ``` -Note that, production in this case is just an environment with a stage called `prod`. You can call it anything you like. Serverless Framework will simply create another version of your app with a completely new set of resources. You can learn more about this in our chapter on [Stages in Serverless Framework]({% link _chapters/stages-in-serverless-framework.md %}). +Note that, production in this case is just an environment with a stage called `prod`. You can call it anything you like. Serverless Framework will simply create another version of your app with a completely new set of resources. You can learn more about this in our chapter on [Stages in Serverless Framework]({% link _archives/stages-in-serverless-framework.md %}). -Next, you can head back to our main guide and [follow the frontend section]({% link _chapters/create-a-new-reactjs-app.md %}). Just remember to use the resources that were created here in your React.js app config! +Next, you can go to our main guide and [follow the frontend section]({% link _chapters/create-a-new-reactjs-app.md %}). Just remember to use the resources that were created here in your React.js app config! diff --git a/_chapters/deploying-a-react-app-to-aws.md b/_archives/deploying-a-react-app-to-aws.md similarity index 90% rename from _chapters/deploying-a-react-app-to-aws.md rename to _archives/deploying-a-react-app-to-aws.md index 25fea8a64..0830005e0 100644 --- a/_chapters/deploying-a-react-app-to-aws.md +++ b/_archives/deploying-a-react-app-to-aws.md @@ -4,9 +4,11 @@ title: Deploy a React App to AWS date: 2017-02-05 00:00:00 lang: en description: Tutorial on how to host a React.js single page application on AWS S3 and CloudFront. -redirect_from: /chapters/deploy-the-frontend.html comments_id: deploy-the-frontend/39 ref: deploy-the-frontend +redirect_from: + - /chapters/deploying-a-react-app-to-aws.html + - /chapters/deploy-the-frontend.html --- In this section we'll be looking at how to deploy your React app as a static website on AWS. diff --git a/_chapters/deploying-a-react-app-to-netlify.md b/_archives/deploying-a-react-app-to-netlify.md similarity index 87% rename from _chapters/deploying-a-react-app-to-netlify.md rename to _archives/deploying-a-react-app-to-netlify.md index 7cd111c2d..e6c791e78 100644 --- a/_chapters/deploying-a-react-app-to-netlify.md +++ b/_archives/deploying-a-react-app-to-netlify.md @@ -4,9 +4,11 @@ title: Deploying a React App to Netlify date: 2020-11-03 00:00:00 lang: en description: In this chapter we'll be looking at hosting your React app on Netlify. Our React app is a static site and it's pretty simple to host them. -redirect_from: /chapters/hosting-your-react-app.html ref: deploying-a-react-app-to-netlify comments_id: hosting-your-react-app/2177 +redirect_from: + - /chapters/deploying-a-react-app-to-netlify.html + - /chapters/hosting-your-react-app.html --- In this section we'll be looking at how to deploy your React.js app as a static website to [Netlify](https://www.netlify.com). You'll recall that in the [main part of the guide]({% link _chapters/create-a-new-reactjs-app.md %}) we used the SST [`ReactStaticSite`]({{ site.docs_url }}/constructs/ReactStaticSite) construct to deploy our React app to AWS. @@ -19,6 +21,6 @@ The basic setup we are going to be using will look something like this: 2. Configure custom domains 3. Create a CI/CD pipeline for our app -We also have another alternative version of this where we deploy our React app to S3 and we use CloudFront as a CDN in front of it. We use Route 53 to configure our custom domain. We also need to configure the www version of our domain and this needs another S3 and CloudFront distribution. The entire process can be a bit cumbersome. But if you are looking for a way to deploy and host the React app in your AWS account, we have an Extra Credit chapter on this — [Deploying a React app on AWS]({% link _chapters/deploying-a-react-app-to-aws.md %}). +We also have another alternative version of this where we deploy our React app to S3 and we use CloudFront as a CDN in front of it. We use Route 53 to configure our custom domain. We also need to configure the www version of our domain and this needs another S3 and CloudFront distribution. The entire process can be a bit cumbersome. But if you are looking for a way to deploy and host the React app in your AWS account, we have an Extra Credit chapter on this — [Deploying a React app on AWS]({% link _archives/deploying-a-react-app-to-aws.md %}). Let's get started by creating our project on Netlify. diff --git a/_chapters/deploying-only-updated-services.md b/_archives/deploying-only-updated-services.md similarity index 98% rename from _chapters/deploying-only-updated-services.md rename to _archives/deploying-only-updated-services.md index 941ed8bb1..87d4f1b1a 100644 --- a/_chapters/deploying-only-updated-services.md +++ b/_archives/deploying-only-updated-services.md @@ -4,6 +4,7 @@ title: Deploying Only Updated Services description: In this chapter we look at how to speed up deployments to our monorepo serverless app by only redeploying the services that have been updated. We can do this by relying on the check Serverless Framework does. Or by looking at the Git log for the directories that have been updated. date: 2019-10-02 00:00:00 comments_id: deploying-only-updated-services/1321 +redirect_from: /chapters/deploying-only-updated-services.html --- Once you are repeatedly deploying your serverless application, you might notice that the Serverless deployments are not very fast. This is especially true if your app has a ton of service. There are a couple of things you can do here to speed up your builds. One of them is to only deploy the services that've been updated. diff --git a/_chapters/deploying-to-multiple-aws-accounts.md b/_archives/deploying-to-multiple-aws-accounts.md similarity index 98% rename from _chapters/deploying-to-multiple-aws-accounts.md rename to _archives/deploying-to-multiple-aws-accounts.md index c82a30377..d93cb0caa 100644 --- a/_chapters/deploying-to-multiple-aws-accounts.md +++ b/_archives/deploying-to-multiple-aws-accounts.md @@ -4,6 +4,7 @@ title: Deploying to Multiple AWS Accounts description: Once you've configured the environments in your serverless app across multiple AWS accounts, you'll want to deploy them. In this chapter, we look at how to create the AWS credentials and manage the environments using Seed. date: 2019-09-30 00:00:00 comments_id: deploying-to-multiple-aws-accounts/1322 +redirect_from: /chapters/deploying-to-multiple-aws-accounts.html --- Now that you have a couple of AWS accounts created and your resources have been parameterized, let's look at how to deploy them. In this chapter, we'll deploy the following: diff --git a/_chapters/dynamically-generate-social-share-images-with-serverless.md b/_archives/dynamically-generate-social-share-images-with-serverless.md similarity index 99% rename from _chapters/dynamically-generate-social-share-images-with-serverless.md rename to _archives/dynamically-generate-social-share-images-with-serverless.md index a107d00cb..dfee877d3 100644 --- a/_chapters/dynamically-generate-social-share-images-with-serverless.md +++ b/_archives/dynamically-generate-social-share-images-with-serverless.md @@ -7,6 +7,7 @@ description: In this chapter we'll look at how to create a serverless service th repo: https://github.com/sst/social-cards ref: dynamically-generate-social-share-images-with-serverless comments_id: dynamically-generate-social-share-images-with-serverless/2419 +redirect_from: /chapters/dynamically-generate-social-share-images-with-serverless.html --- In this chapter we'll look at how to dynamically generate social share images or open graph (OG) images with serverless. diff --git a/_chapters/environments-in-create-react-app.md b/_archives/environments-in-create-react-app.md similarity index 97% rename from _chapters/environments-in-create-react-app.md rename to _archives/environments-in-create-react-app.md index f761f6b91..119d9a87d 100644 --- a/_chapters/environments-in-create-react-app.md +++ b/_archives/environments-in-create-react-app.md @@ -4,6 +4,7 @@ title: Environments in Create React App description: Use custom environment variables in Create React App to add staging, dev, or production environments to your React app. Custom environment variables are supported by default in Create React App. And by editing our NPM scripts we can easily deploy to multiple environments. date: 2018-04-18 00:00:00 comments_id: environments-in-create-react-app/30 +redirect_from: /chapters/environments-in-create-react-app.html --- While developing your frontend React app and working with an API backend, you'll often need to create multiple environments to work with. For example, you might have an environment called dev that might be connected to the dev stage of your serverless backend. This is to ensure that you are working in an environment that is isolated from your production version. @@ -51,7 +52,7 @@ The first thing we can do is to configure our build system with the `REACT_APP_S } ``` -Recall that the `YOUR_S3_DEPLOY_BUCKET_NAME` is the S3 bucket we created to host our React app back in the [Create an S3 bucket]({% link _chapters/create-an-s3-bucket.md %}) chapter. And `YOUR_CF_DISTRIBUTION_ID` and `YOUR_WWW_CF_DISTRIBUTION_ID` are the CloudFront Distributions for the [apex]({% link _chapters/create-a-cloudfront-distribution.md %}) and [www]({% link _chapters/setup-www-domain-redirect.md %}) domains. +Recall that the `YOUR_S3_DEPLOY_BUCKET_NAME` is the S3 bucket we created to host our React app back in the [Create an S3 bucket]({% link _archives/create-an-s3-bucket.md %}) chapter. And `YOUR_CF_DISTRIBUTION_ID` and `YOUR_WWW_CF_DISTRIBUTION_ID` are the CloudFront Distributions for the [apex]({% link _archives/create-a-cloudfront-distribution.md %}) and [www]({% link _archives/setup-www-domain-redirect.md %}) domains. Here we only have one environment and we use it for our local development and on live. The `npm start` command runs our local server and `npx sst deploy` command deploys our app to live. diff --git a/_chapters/environments-in-serverless-apps.md b/_archives/environments-in-serverless-apps.md similarity index 97% rename from _chapters/environments-in-serverless-apps.md rename to _archives/environments-in-serverless-apps.md index 02bad331e..617da8896 100644 --- a/_chapters/environments-in-serverless-apps.md +++ b/_archives/environments-in-serverless-apps.md @@ -4,6 +4,7 @@ title: Environments in Serverless Apps description: In this chapter, we are going to be looking at the best practices of configuring environments for your serverless app. Serverless allows us to easily create new environments since it's completely pay per use. This makes it ideal for creating ephemeral environments for new features, bugfixes, or pull requests. date: 2019-09-30 00:00:00 comments_id: environments-in-serverless-apps/1323 +redirect_from: /chapters/environments-in-serverless-apps.html --- In this section, we are going to be looking at the best practices of configuring environments for your serverless app. But before we do that, let's quickly go over some of the concepts involved. diff --git a/_chapters/facebook-login-with-cognito-using-aws-amplify.md b/_archives/facebook-login-with-cognito-using-aws-amplify.md similarity index 98% rename from _chapters/facebook-login-with-cognito-using-aws-amplify.md rename to _archives/facebook-login-with-cognito-using-aws-amplify.md index 37163c86a..06a849fa7 100644 --- a/_chapters/facebook-login-with-cognito-using-aws-amplify.md +++ b/_archives/facebook-login-with-cognito-using-aws-amplify.md @@ -5,6 +5,7 @@ description: To allow your users to login using Facebook to your serverless Reac date: 2018-04-19 00:00:00 code: facebook-login comments_id: facebook-login-with-cognito-using-aws-amplify/466 +redirect_from: /chapters/facebook-login-with-cognito-using-aws-amplify.html --- In our guide so far we have used the [Cognito User Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html) to sign up users to our [demo notes app](https://demo.sst.dev). This means that our users have to sign up for an account with their email and password. But you might want your users to use their Facebook or Google account to sign up for your app. It also means that your users won't have to remember another email and password combination for the sites they use. In this chapter we will look at how to add a _"Login with Facebook"_ option to our demo app. @@ -46,7 +47,7 @@ Finally, head over to **Settings** > **Basic** and make a note of your **App ID* We are going to need this when we configure the AWS and React portion of our app. -Next we are going to use [Cognito Identity Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html) to federate our identities. This means that when a user signs up with their Facebook account, they will get added to our Identity Pool. And our serverless backend API will get an Id that we can use. This Id will remain the same if the user signs in later at any point. If you are a little confused about how the Identity Pool is different from the User Pool, you can take a quick look at our [Cognito user pool vs identity pool]({% link _chapters/cognito-user-pool-vs-identity-pool.md %}) chapter. +Next we are going to use [Cognito Identity Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html) to federate our identities. This means that when a user signs up with their Facebook account, they will get added to our Identity Pool. And our serverless backend API will get an Id that we can use. This Id will remain the same if the user signs in later at any point. If you are a little confused about how the Identity Pool is different from the User Pool, you can take a quick look at our [Cognito user pool vs identity pool]({% link _archives/cognito-user-pool-vs-identity-pool.md %}) chapter. ### Add Facebook as an Authentication Provider diff --git a/_chapters/frontend-workflow-in-netlify.md b/_archives/frontend-workflow-in-netlify.md similarity index 97% rename from _chapters/frontend-workflow-in-netlify.md rename to _archives/frontend-workflow-in-netlify.md index e08b74ce5..3cbd6110d 100644 --- a/_chapters/frontend-workflow-in-netlify.md +++ b/_archives/frontend-workflow-in-netlify.md @@ -4,14 +4,15 @@ title: Frontend Workflow in Netlify date: 2018-03-29 00:00:00 lang: en description: There are three steps that are a part of workflow for a Create React App configured with Netlify. To work on new features create a new branch and enable branch deployments. And merge to master to deploy to production. Finally, publish an old deployment through the Netlify console to rollback in production. +ref: frontend-workflow-in-netlify +comments_id: frontend-workflow/192 redirect_from: - /chapters/update-the-app.html - /chapters/frontend-workflow.html -ref: frontend-workflow-in-netlify -comments_id: frontend-workflow/192 + - /chapters/frontend-workflow-in-netlify.html --- -Now that we have our [Netlify build script configured]({% link _chapters/create-a-netlify-build-script.md %}), let's go over what our development workflow with Netlify will look like. +Now that we have our [Netlify build script configured]({% link _archives/create-a-netlify-build-script.md %}), let's go over what our development workflow with Netlify will look like. ### Working in a Dev Branch diff --git a/_chapters/handle-api-gateway-cors-errors.md b/_archives/handle-api-gateway-cors-errors.md similarity index 98% rename from _chapters/handle-api-gateway-cors-errors.md rename to _archives/handle-api-gateway-cors-errors.md index 3e1fc92f8..0f1e8d56e 100644 --- a/_chapters/handle-api-gateway-cors-errors.md +++ b/_archives/handle-api-gateway-cors-errors.md @@ -6,6 +6,7 @@ lang: en ref: handle-api-gateway-cors-errors description: We need to add the CORS headers to our serverless API Gateway endpoint to handle 4xx and 5xx errors. This is to handle the case where our Lambda functions are not being invoked. comments_id: handle-api-gateway-cors-errors/780 +redirect_from: /chapters/handle-api-gateway-cors-errors.html --- Our Serverless Framework app is now using infrastructure as code to configure its resources. Though we need to go over one small detail before moving forward. diff --git a/_chapters/handle-forgot-and-reset-password.md b/_archives/handle-forgot-and-reset-password.md similarity index 99% rename from _chapters/handle-forgot-and-reset-password.md rename to _archives/handle-forgot-and-reset-password.md index 45acd0772..14d739af2 100644 --- a/_chapters/handle-forgot-and-reset-password.md +++ b/_archives/handle-forgot-and-reset-password.md @@ -5,6 +5,7 @@ description: Use the AWS Amplify Auth.forgotPassword method to support forgot pa date: 2018-04-14 00:00:00 code: user-management comments_id: handle-forgot-and-reset-password/506 +redirect_from: /chapters/handle-forgot-and-reset-password.html --- In our [serverless notes app](https://demo.sst.dev) we've used [Cognito User Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html) to sign up and login our users. In the frontend we've used [AWS Amplify](https://aws-amplify.github.io/) in our React app. However, if our users have forgotten their passwords, we need to have a way for them to reset their password. In this chapter we will look at how to do this. diff --git a/_chapters/how-to-add-authentication-to-a-serverless-app.md b/_archives/how-to-add-authentication-to-a-serverless-app.md similarity index 95% rename from _chapters/how-to-add-authentication-to-a-serverless-app.md rename to _archives/how-to-add-authentication-to-a-serverless-app.md index 18e65f618..9f8bde5f5 100644 --- a/_chapters/how-to-add-authentication-to-a-serverless-app.md +++ b/_archives/how-to-add-authentication-to-a-serverless-app.md @@ -6,6 +6,7 @@ lang: en description: In this chapter we'll look at how authentication works for serverless apps in AWS. We'll be looking at the differences between authentication and authorization, the various authentication options, and go into detail for IAM and Cognito. ref: how-to-add-authentication-to-a-serverless-app comments_id: how-to-add-authentication-to-a-serverless-app/2433 +redirect_from: /chapters/how-to-add-authentication-to-a-serverless-app.html --- In this section we'll look at how authentication works for serverless apps in AWS. Over the course of the next few chapters we'll be looking at the various authentication options. @@ -22,11 +23,11 @@ When I waved my badge in front of the black box, the system was able to verify w ### Adding Authentication in Serverless -You can use one of AWS's built-in authentication methods in your [API Gateway](https://aws.amazon.com/api-gateway/) or [AppSync]({% link _chapters/what-is-aws-appsync.md %}) APIs. Or if you need some extra features, there are plenty of third-party services, some of which you can self-host. +You can use one of AWS's built-in authentication methods in your [API Gateway](https://aws.amazon.com/api-gateway/) or [AppSync]({% link _archives/what-is-aws-appsync.md %}) APIs. Or if you need some extra features, there are plenty of third-party services, some of which you can self-host. 1. **IAM** - [Identity and Access Management]({% link _chapters/what-is-iam.md %}), or IAM, is AWS's authentication system. It's used for authentication and authorization for management tasks, like the AWS Console, CLI, or calling from one resource to another. + [Identity and Access Management]({% link _archives/what-is-iam.md %}), or IAM, is AWS's authentication system. It's used for authentication and authorization for management tasks, like the AWS Console, CLI, or calling from one resource to another. 2. **Cognito** @@ -48,9 +49,9 @@ Let’s look at a couple of these options in detail. ### AWS IAM -[AWS IAM]({% link _chapters/what-is-iam.md %}) is how AWS controls access to resources natively. When you log in to the AWS console or use the CLI, your request is authorized by IAM. +[AWS IAM]({% link _archives/what-is-iam.md %}) is how AWS controls access to resources natively. When you log in to the AWS console or use the CLI, your request is authorized by IAM. -You can use IAM to control access to an API Gateway or [AppSync API]({% link _chapters/what-is-aws-appsync.md %}) by attaching a role to the resource that's making requests. At runtime, the resource will assume the role, which will allow it to make requests to your API depending on what permissions you've given to the role. +You can use IAM to control access to an API Gateway or [AppSync API]({% link _archives/what-is-aws-appsync.md %}) by attaching a role to the resource that's making requests. At runtime, the resource will assume the role, which will allow it to make requests to your API depending on what permissions you've given to the role. For example, if you create a Lambda function that needs access to your AppSync API, you would first define a role that provides that permission and allows the Lambda service to assume the role. When your function starts, it will tell IAM that it wants to assume the role that you defined. IAM will authenticate the request by verifying that it came from a Lambda function and that Lambdas are allowed to assume the role. Once the function has been authenticated, IAM will provide it with temporary credentials to use when making requests to AWS services. @@ -85,7 +86,7 @@ Identity Pools provide a service called *identity federation*. Identity federati The Identity Pool will then verify the token came from a valid authentication service. Once it's satisfied that the token is valid, it will return temporary AWS credentials that use an IAM role that you've attached to the Identity Pool. So in a way the auth providers here are handling authentication. While, the Identity Pool is managing authorization. -For a detailed comparison, check out our chapter on [Cognito User Pool vs Identity Pool]({% link _chapters/cognito-user-pool-vs-identity-pool.md %}). +For a detailed comparison, check out our chapter on [Cognito User Pool vs Identity Pool]({% link _archives/cognito-user-pool-vs-identity-pool.md %}). #### Pricing @@ -120,4 +121,4 @@ Finally, AWS automatically handles User Pool token validation. If you're using a ### Next Steps -This chapter should give you a good high-level overview of how to handle authentication in serverless apps. In the next few chapters we'll be looking at specific examples of how to use various authentication providers. Starting with [how to use Cognito to add authentication to your serverless app]({% link _chapters/using-cognito-to-add-authentication-to-a-serverless-app.md %}). +This chapter should give you a good high-level overview of how to handle authentication in serverless apps. In the next few chapters we'll be looking at specific examples of how to use various authentication providers. Starting with [how to use Cognito to add authentication to your serverless app]({% link _archives/using-cognito-to-add-authentication-to-a-serverless-app.md %}). diff --git a/_chapters/invoke-api-gateway-endpoints-locally.md b/_archives/invoke-api-gateway-endpoints-locally.md similarity index 98% rename from _chapters/invoke-api-gateway-endpoints-locally.md rename to _archives/invoke-api-gateway-endpoints-locally.md index 5c3542fbf..db10b7e48 100644 --- a/_chapters/invoke-api-gateway-endpoints-locally.md +++ b/_archives/invoke-api-gateway-endpoints-locally.md @@ -4,6 +4,7 @@ title: Invoke API Gateway Endpoints Locally description: In this chapter we look at testing API Gateway endpoints locally in your serverless app. We also look at how to mock Cognito authentication info. We'll create a local web server for all the services in our monorepo app. date: 2019-10-02 00:00:00 comments_id: invoke-api-gateway-endpoints-locally/1324 +redirect_from: /chapters/invoke-api-gateway-endpoints-locally.html --- Our notes app backend has an API Gateway endpoint. We want to be able to develop against this endpoint locally. To do this we'll use the [**serverless-offline plugin**](https://github.com/dherault/serverless-offline) to start a local web server. diff --git a/_chapters/invoke-lambda-functions-locally.md b/_archives/invoke-lambda-functions-locally.md similarity index 98% rename from _chapters/invoke-lambda-functions-locally.md rename to _archives/invoke-lambda-functions-locally.md index 41a3248fc..178514932 100644 --- a/_chapters/invoke-lambda-functions-locally.md +++ b/_archives/invoke-lambda-functions-locally.md @@ -4,6 +4,7 @@ title: Invoke Lambda Functions Locally description: In this chapter we look at how to develop and test Lambda functions locally. We look at the different types of event payloads to use for HTTP based Lambda functions. date: 2019-10-02 00:00:00 comments_id: invoke-lambda-functions-locally/1325 +redirect_from: /chapters/invoke-lambda-functions-locally.html --- After you finish creating a Lambda function, you want to first run it locally. diff --git a/_chapters/load-secrets-from-env.md b/_archives/load-secrets-from-env.md similarity index 96% rename from _chapters/load-secrets-from-env.md rename to _archives/load-secrets-from-env.md index d31c40820..ba8e02a66 100644 --- a/_chapters/load-secrets-from-env.md +++ b/_archives/load-secrets-from-env.md @@ -4,9 +4,11 @@ title: Load Secrets from .env date: 2018-03-08 00:00:00 lang: en description: We should not store secret environment variables in our serverless.yml. For this we will use a .env file that will not be checked into source control. This file will be loaded automatically using the serverless-dotenv-plugin. -redirect_from: /chapters/load-secrets-from-env-yml.html ref: load-secrets-from-env-yml comments_id: load-secrets-from-env-yml/171 +redirect_from: + - /chapters/load-secrets-from-env.html + - /chapters/load-secrets-from-env-yml.html --- As we had previously mentioned, we do not want to store our secret environment variables in our code. In our case it is the Stripe secret key. In this chapter, we'll look at how to do that. diff --git a/_chapters/manage-aws-accounts-using-aws-organizations.md b/_archives/manage-aws-accounts-using-aws-organizations.md similarity index 98% rename from _chapters/manage-aws-accounts-using-aws-organizations.md rename to _archives/manage-aws-accounts-using-aws-organizations.md index a3523e0ea..fbca1c617 100644 --- a/_chapters/manage-aws-accounts-using-aws-organizations.md +++ b/_archives/manage-aws-accounts-using-aws-organizations.md @@ -4,6 +4,7 @@ title: Manage AWS Accounts Using AWS Organizations description: In this chapter we look at how to create multiple AWS accounts for the environments in your Serverless Framework app. We'll be using the AWS Organizations console for this. date: 2019-09-30 00:00:00 comments_id: manage-aws-accounts-using-aws-organizations/1326 +redirect_from: /chapters/manage-aws-accounts-using-aws-organizations.html --- With AWS Organizations, you can create and manage all the AWS accounts in your master account. In this chapter we'll look at how to create multiple AWS accounts for the environments in our serverless app. diff --git a/_chapters/manage-environment-related-config.md b/_archives/manage-environment-related-config.md similarity index 96% rename from _chapters/manage-environment-related-config.md rename to _archives/manage-environment-related-config.md index e7d612c80..e33d3301f 100644 --- a/_chapters/manage-environment-related-config.md +++ b/_archives/manage-environment-related-config.md @@ -3,13 +3,15 @@ layout: post title: Manage Environment Related Config description: In this chapter we'll look at how our services will connect to each other while they are deployed across multiple environments. date: 2019-10-02 00:00:00 -redirect_from: /chapters/use-environment-variables-in-lambda-functions.html comments_id: manage-environment-related-config/1327 +redirect_from: + - /chapters/manage-environment-related-config.html + - /chapters/use-environment-variables-in-lambda-functions.html --- In this chapter we'll look at how our services will connect to each other while they are deployed across multiple environments. -Let's quickly review the setup that we've created back in the [Organizing services chapter]({% link _chapters/organizing-serverless-projects.md %}). +Let's quickly review the setup that we've created back in the [Organizing services chapter]({% link _archives/organizing-serverless-projects.md %}). 1. We have two repos — [serverless-stack-demo-ext-resources]({{ site.backend_ext_resources_github_repo }}) and [serverless-stack-demo-ext-api]({{ site.backend_ext_api_github_repo }}). One has our infrastructure specific resources, while the other has all our Lambda functions. 2. The `serverless-stack-demo-ext-resources` repo is deployed a couple of long lived environments; like `dev` and `prod`. diff --git a/_chapters/manage-environments-in-create-react-app.md b/_archives/manage-environments-in-create-react-app.md similarity index 98% rename from _chapters/manage-environments-in-create-react-app.md rename to _archives/manage-environments-in-create-react-app.md index 00098b226..ca107614d 100644 --- a/_chapters/manage-environments-in-create-react-app.md +++ b/_archives/manage-environments-in-create-react-app.md @@ -6,6 +6,7 @@ lang: en description: To configure environments in our Create React App, we will create a new custom environment variable. We will use this as a part of our build process and set the config based on environment that we are targeting. ref: manage-environments-in-create-react-app comments_id: manage-environments-in-create-react-app/182 +redirect_from: /chapters/manage-environments-in-create-react-app.html --- We want to ensure that our React.js app connects to the right version of the backend resources when we deploy it to different environments. Let's look at how to do that. diff --git a/_chapters/manage-user-accounts-in-aws-amplify.md b/_archives/manage-user-accounts-in-aws-amplify.md similarity index 95% rename from _chapters/manage-user-accounts-in-aws-amplify.md rename to _archives/manage-user-accounts-in-aws-amplify.md index 784a35fa1..456ac6199 100644 --- a/_chapters/manage-user-accounts-in-aws-amplify.md +++ b/_archives/manage-user-accounts-in-aws-amplify.md @@ -5,6 +5,7 @@ description: In the next series of chapters we will look at how to manage user a date: 2018-04-13 00:00:00 code: user-management comments_id: manage-user-accounts-in-aws-amplify/505 +redirect_from: /chapters/manage-user-accounts-in-aws-amplify.html --- If you've followed along with [the first part of SST](/#the-basics) guide, you might be looking to add ways your users can better manage their accounts. This includes the ability to: diff --git a/_chapters/mapping-cognito-identity-id-and-user-pool-id.md b/_archives/mapping-cognito-identity-id-and-user-pool-id.md similarity index 96% rename from _chapters/mapping-cognito-identity-id-and-user-pool-id.md rename to _archives/mapping-cognito-identity-id-and-user-pool-id.md index d158b22ec..c7c61e969 100644 --- a/_chapters/mapping-cognito-identity-id-and-user-pool-id.md +++ b/_archives/mapping-cognito-identity-id-and-user-pool-id.md @@ -4,6 +4,7 @@ title: Mapping Cognito Identity Id and User Pool Id description: Access a user's Cognito User Pool user Id in an AWS Lambda function that is secured using AWS IAM and Federated Identities using the event.requestContext.identity.cognitoAuthenticationProvider string. date: 2018-04-09 00:00:00 comments_id: mapping-cognito-identity-id-and-user-pool-id/500 +redirect_from: /chapters/mapping-cognito-identity-id-and-user-pool-id.html --- If you are using the Cognito User Pool to manage your users while using the Identity Pool to secure your AWS resources; you might run into an interesting issue. How do you find the user's User Pool User Id in your Lambda function? @@ -15,7 +16,7 @@ You might recall ([from the chapters where we work with our Lambda functions]({% 1. Authenticate through your User Pool 2. And then federate their identity through the Identity Pool -At this second step, their User Pool information is no longer available to us. To better understand this flow you can take a look at the [Cognito user pool vs identity pool]({% link _chapters/cognito-user-pool-vs-identity-pool.md %}) chapter. But in a nutshell, you can have multiple authentication providers at step 1 and the Identity Pool just ensures that they are all given a _global_ user id that you can use. +At this second step, their User Pool information is no longer available to us. To better understand this flow you can take a look at the [Cognito user pool vs identity pool]({% link _archives/cognito-user-pool-vs-identity-pool.md %}) chapter. But in a nutshell, you can have multiple authentication providers at step 1 and the Identity Pool just ensures that they are all given a _global_ user id that you can use. ### Finding the User Pool User Id diff --git a/_chapters/monitor-usage-for-environments.md b/_archives/monitor-usage-for-environments.md similarity index 97% rename from _chapters/monitor-usage-for-environments.md rename to _archives/monitor-usage-for-environments.md index 45592aa07..b75c10e09 100644 --- a/_chapters/monitor-usage-for-environments.md +++ b/_archives/monitor-usage-for-environments.md @@ -4,6 +4,7 @@ title: Monitor Usage for Environments description: AWS Organization gives us a good way to manage cost and usage for our AWS accounts. It allows us to easily manage the environments of our serverless app. date: 2019-10-02 00:00:00 comments_id: monitor-usage-for-environments/1328 +redirect_from: /chapters/monitor-usage-for-environments.html --- So far we've split the environments for our serverless app across two AWS accounts. But before we go ahead and look at the development workflow, let's look at how to manage the cost and usage for them. diff --git a/_chapters/organizing-serverless-projects.md b/_archives/organizing-serverless-projects.md similarity index 98% rename from _chapters/organizing-serverless-projects.md rename to _archives/organizing-serverless-projects.md index 7ca382c20..e97c312f8 100644 --- a/_chapters/organizing-serverless-projects.md +++ b/_archives/organizing-serverless-projects.md @@ -4,6 +4,7 @@ title: Organizing Serverless Projects description: In this chapter we will look at the common patterns for organizing Serverless Framework applications. We'll go over the microservice, monorepo, multi-repo, and monolith pattern. Finally, we'll make some recommendations on the best way to organize real world Serverless apps. date: 2019-09-29 00:00:00 comments_id: organizing-serverless-projects/350 +redirect_from: /chapters/organizing-serverless-projects.html --- Once your serverless projects start to grow, you are faced with some choices on how to organize your growing projects. In this chapter we'll examine some of the most common ways to structure your projects at a services and application (multiple services) level. @@ -68,7 +69,7 @@ A couple of things to notice here: 5. The `package.json` (and the `node_modules/` dir) are at the root of the repo. However, it is fairly common to have a separate `package.json` inside each service directory. 6. The `libs/` dir is just to illustrate that any common code that might be used across all services can be placed in here. 7. To deploy this application you are going to need to run `serverless deploy` separately in each of the services. -8. [Environments (or stages)]({% link _chapters/stages-in-serverless-framework.md %}) need to be co-ordinated across all the different services. So if your team is using a `dev`, `staging`, and `prod` environment, then you are going to need to define the specifics of this in each of the services. +8. [Environments (or stages)]({% link _archives/stages-in-serverless-framework.md %}) need to be co-ordinated across all the different services. So if your team is using a `dev`, `staging`, and `prod` environment, then you are going to need to define the specifics of this in each of the services. #### Advantages of Monorepo @@ -105,7 +106,7 @@ A couple of things to watch out for with the multi-repo pattern. The CloudFormation template is invalid: Template format error: Number of resources, 201, is greater than maximum allowed, 200 ``` - + Note that, [AWS recently increased this limit to 500](https://aws.amazon.com/about-aws/whats-new/2020/10/aws-cloudformation-now-supports-increased-limits-on-five-service-quotas/). Even with the disadvantages the multi-repo pattern does have its place. We have come across cases where some infrastructure related pieces (setting up DynamoDB, Cognito, etc) is done in a service that is placed in a separate repo. And since this typically doesn't need a lot of code or even share anything with the rest of your application, it can live on its own. So in effect you can run a multi-repo setup where the standalone repos are for your _infrastructure_ and your _API endpoints_ live in a microservice + monorepo setup. @@ -176,6 +177,6 @@ On the other hand, changes are going to happen less frequently in the **serverle So if you have a service that doesn't make sense to replicate in an ephemeral environment, we would suggest moving it to the repo with all the infrastructure services. This is what we have seen most teams do. And this setup scales well as your project and team grows. -Note that, we build on this monorepo setup further by using [Lerna](https://lerna.js.org) and [Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) in our [Using Lerna and Yarn Workspaces with Serverless]({% link _chapters/using-lerna-and-yarn-workspaces-with-serverless.md %}) extra credit chapter. +Note that, we build on this monorepo setup further by using [Lerna](https://lerna.js.org) and [Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) in our [Using Lerna and Yarn Workspaces with Serverless]({% link _archives/using-lerna-and-yarn-workspaces-with-serverless.md %}) extra credit chapter. Now that we have figured out how to organize our application into repos, let's look at how we split our app into the various services. We'll start with creating a separate service for our DynamoDB tables. diff --git a/_chapters/package-lambdas-with-serverless-bundle.md b/_archives/package-lambdas-with-serverless-bundle.md similarity index 98% rename from _chapters/package-lambdas-with-serverless-bundle.md rename to _archives/package-lambdas-with-serverless-bundle.md index a6717b4a4..ba05624c7 100644 --- a/_chapters/package-lambdas-with-serverless-bundle.md +++ b/_archives/package-lambdas-with-serverless-bundle.md @@ -4,6 +4,7 @@ title: Package Lambdas with serverless-bundle description: The serverless-bundle plugin uses Webpack to generate optimized packages for ES6 or TypeScript Lambda functions without having to maintain any Webpack configs or plugins. date: 2018-04-12 12:00:00 comments_id: package-lambdas-with-serverless-bundle/1150 +redirect_from: /chapters/package-lambdas-with-serverless-bundle.html --- AWS Lambda functions are stored as zip files in an S3 bucket. They are loaded up onto a container when the function is invoked. The time it takes to do this is called the cold start time. If a function has been recently invoked, the container is kept around. In this case, your functions get invoked a lot quicker and this delay is referred to as the warm start time. One of the factors that affects cold starts, is the size of your Lambda function package. The larger the package, the longer it takes to invoke your Lambda function. diff --git a/_chapters/parameterize-serverless-resources-names.md b/_archives/parameterize-serverless-resources-names.md similarity index 98% rename from _chapters/parameterize-serverless-resources-names.md rename to _archives/parameterize-serverless-resources-names.md index 55ef1a305..e81721a8a 100644 --- a/_chapters/parameterize-serverless-resources-names.md +++ b/_archives/parameterize-serverless-resources-names.md @@ -4,6 +4,7 @@ title: Parameterize Serverless Resources Names description: When deploying your Serverless Framework app to multiple environments, we need to ensure the resource names do not thrash across environments. To do this we'll be parameterizing our resource names with the name of the stage we are deploying to. date: 2019-09-30 00:00:00 comments_id: parameterize-serverless-resources-names/1329 +redirect_from: /chapters/parameterize-serverless-resources-names.html --- When deploying multiple environments, some into the same AWS account, some across multiple AWS accounts, we need to ensure the resource names do not thrash across environments. For example, in our `checkout-api` service, we have a Lambda function called `checkout`. Now, if two developers are working on two different features, one deploys to the `featureA` environment and one deploys to the `featureB` environment, and both environments reside in the `Dev` AWS account, only one environment can be successfully deployed. The second environment will get an error indicating that a Lambda function with the name `checkout` already exists. diff --git a/_chapters/promoting-to-production.md b/_archives/promoting-to-production.md similarity index 98% rename from _chapters/promoting-to-production.md rename to _archives/promoting-to-production.md index e9ed27d81..c8c9bbaa5 100644 --- a/_chapters/promoting-to-production.md +++ b/_archives/promoting-to-production.md @@ -4,6 +4,7 @@ title: Promoting to Production description: In this chapter we'll look at the process of promoting your serverless app to production using Seed. We'll look at why a manual promote is recommended and how change sets can help us review our changes. date: 2019-10-02 00:00:00 comments_id: promoting-to-production/1330 +redirect_from: /chapters/promoting-to-production.html --- Now that our new feature has been tested and merged to master, we are ready to promote it to production. We are going to do so by promoting our `dev` stage to `prod`. diff --git a/_chapters/rollback-changes.md b/_archives/rollback-changes.md similarity index 96% rename from _chapters/rollback-changes.md rename to _archives/rollback-changes.md index ca15ae93f..f4c2a9f04 100644 --- a/_chapters/rollback-changes.md +++ b/_archives/rollback-changes.md @@ -4,6 +4,7 @@ title: Rollback Changes description: In this chapter we look at how to rollback services in our monorepo serverless app. If we are rolling back services with dependencies, we need to make sure to roll them back in the opposite order they were deployed in. date: 2019-10-02 00:00:00 comments_id: rollback-changes/1331 +redirect_from: /chapters/rollback-changes.html --- So we've worked on a new feature, deployed it to a feature branch, created a PR for it, merged it to master, and promoted it to production! We are almost done going over the workflow. But before we move on we want to make sure that you are able to rollback your serverless deployments in case there is a problem. We think this is a critical aspect of your CI/CD pipeline. In this chapter we’ll look at what the right rollback strategy is for your Serverless apps. @@ -26,7 +27,7 @@ Notice a new build is triggered for the `prod` stage. ### Rollback infrastructure change -In our monorepo setup, our app is made up of multiple services, and some services are dependent on each other. These dependencies require the services to be deployed in a specific order. Previously, we talked about how to [deploy services with dependencies]({% link _chapters/deploy-a-serverless-app-with-dependencies.md %}). We also need to watch out for the deployment order when rolling back a change. +In our monorepo setup, our app is made up of multiple services, and some services are dependent on each other. These dependencies require the services to be deployed in a specific order. Previously, we talked about how to [deploy services with dependencies]({% link _archives/deploy-a-serverless-app-with-dependencies.md %}). We also need to watch out for the deployment order when rolling back a change. Let’s consider a simple example with just two services, `billing-api` and `notify-job`. Where `billing-api` exports an SNS topic named `note-purchased`. Here is an example of `billing-api`’s `serverless.yml`: diff --git a/_chapters/secure-the-apis.md b/_archives/secure-the-apis.md similarity index 96% rename from _chapters/secure-the-apis.md rename to _archives/secure-the-apis.md index 79d7b2e17..11b522361 100644 --- a/_chapters/secure-the-apis.md +++ b/_archives/secure-the-apis.md @@ -6,9 +6,10 @@ lang: en ref: secure-the-apis description: In this chapter we'll be using the `aws_iam` authorizer to secure our serverless APIs. This uses a Cognito Identity Pool and Cognito User Pool for authentication. To identify our users, we'll be using the `cognitoIdentityId` that's passed in through the `event` object in our Lambda function. comments_id: secure-the-apis/2179 +redirect_from: /chapters/secure-the-apis.html --- -Now that we have [created a User Pool]({% link _chapters/create-a-cognito-user-pool.md %}), [Identity Pool and an Auth Role]({% link _chapters/create-a-cognito-identity-pool.md %}); we are ready to use them to secure access to our APIs. +Now that we have [created a User Pool]({% link _archives/create-a-cognito-user-pool.md %}), [Identity Pool and an Auth Role]({% link _archives/create-a-cognito-identity-pool.md %}); we are ready to use them to secure access to our APIs. ### Serverless IAM Auth @@ -157,11 +158,11 @@ userId: event.requestContext.identity.cognitoIdentityId, // The id of the author ":userId": event.requestContext.identity.cognitoIdentityId, ``` -Keep in mind that the `userId` above is the Federated Identity id (or Identity Pool user id). This is not the user id that is assigned in our User Pool. If you want to use the user's User Pool user Id instead, have a look at the [Mapping Cognito Identity Id and User Pool Id]({% link _chapters/mapping-cognito-identity-id-and-user-pool-id.md %}) chapter. +Keep in mind that the `userId` above is the Federated Identity id (or Identity Pool user id). This is not the user id that is assigned in our User Pool. If you want to use the user's User Pool user Id instead, have a look at the [Mapping Cognito Identity Id and User Pool Id]({% link _archives/mapping-cognito-identity-id-and-user-pool-id.md %}) chapter. ### Testing Locally -If you recall the chapters where we first [created our API endpoints]({% link _chapters/add-a-create-note-api.md %}), we were using a set of mock events to test our Lambda functions. We stored these in the `mocks/` directory. +If you recall the chapters where we first [created our API endpoints]({% link _archives/add-a-create-note-api.md %}), we were using a set of mock events to test our Lambda functions. We stored these in the `mocks/` directory. For example, the `create-event.json` looks like this. diff --git a/_chapters/serverless-environment-variables.md b/_archives/serverless-environment-variables.md similarity index 98% rename from _chapters/serverless-environment-variables.md rename to _archives/serverless-environment-variables.md index 5657707b8..9489050cd 100644 --- a/_chapters/serverless-environment-variables.md +++ b/_archives/serverless-environment-variables.md @@ -4,6 +4,7 @@ title: Serverless Environment Variables description: To set environment variables for AWS Lambda using the Serverless Framework we need to use the "environment:" option in the serverless.yml. Serverless Framework also allows you to further configure them using custom variables. date: 2018-04-05 00:00:00 comments_id: serverless-environment-variables/25 +redirect_from: /chapters/serverless-environment-variables.html --- In Node.js we use the `process.env` to get access to environment variables of the current process. In AWS Lambda, we can set environment variables that we can access via the `process.env` object. diff --git a/_chapters/serverless-nodejs-starter.md b/_archives/serverless-nodejs-starter.md similarity index 97% rename from _chapters/serverless-nodejs-starter.md rename to _archives/serverless-nodejs-starter.md index 9ba8e9b57..91451b69f 100644 --- a/_chapters/serverless-nodejs-starter.md +++ b/_archives/serverless-nodejs-starter.md @@ -1,10 +1,12 @@ --- layout: post title: Serverless Node.js Starter -redirect_from: /chapters/serverless-es7-service.html description: A serverless Node.js starter project that adds support for ES6 and TypeScript, linting, and unit tests to your Serverless Framework project. date: 2018-04-12 00:00:00 comments_id: serverless-node-js-starter/22 +redirect_from: + - /chapters/serverless-nodejs-starter.html + - /chapters/serverless-es7-service.html --- Based on what we have gone through in this guide, it makes sense that we have a good starting point for our future projects. For this we created a couple of serverless starter projects that you can use called, [Serverless Node.js Starter](https://github.com/AnomalyInnovations/serverless-nodejs-starter). If you are using TypeScript, we have a starter for you as well, [Serverless TypeScript Starter](https://github.com/AnomalyInnovations/serverless-typescript-starter). We also have a Python version called [Serverless Python Starter](https://github.com/AnomalyInnovations/serverless-python-starter). Our starter projects also work really well with [Seed](https://seed.run); a fully-configured CI/CD pipeline for Serverless Framework. diff --git a/_chapters/set-custom-domains-through-seed.md b/_archives/set-custom-domains-through-seed.md similarity index 97% rename from _chapters/set-custom-domains-through-seed.md rename to _archives/set-custom-domains-through-seed.md index 11ed146db..19cbb2f5b 100644 --- a/_chapters/set-custom-domains-through-seed.md +++ b/_archives/set-custom-domains-through-seed.md @@ -6,6 +6,7 @@ lang: en description: We will use the Seed console to configure our API Gateway endpoints in our serverless project with custom domains. To configure a stage with a custom domain go to the stage settings, select the Route 53 domain, a sub-domain, and the base path. ref: set-custom-domains-through-seed comments_id: set-custom-domains-through-seed/178 +redirect_from: /chapters/set-custom-domains-through-seed.html --- In the main part of our guide, we [used SST to configure custom domains for our serverless app]({% link _chapters/custom-domains-in-serverless-apis.md %}). But if you are using Serverless Framework or want to manage custom domains centrally, [Seed](https://seed.run) gives you another option. diff --git a/_chapters/setting-serverless-environments-variables-in-a-react-app.md b/_archives/setting-serverless-environments-variables-in-a-react-app.md similarity index 86% rename from _chapters/setting-serverless-environments-variables-in-a-react-app.md rename to _archives/setting-serverless-environments-variables-in-a-react-app.md index 443092b87..e9d492a7c 100644 --- a/_chapters/setting-serverless-environments-variables-in-a-react-app.md +++ b/_archives/setting-serverless-environments-variables-in-a-react-app.md @@ -6,6 +6,7 @@ lang: en description: In this chapter we look at how to set environment variables from your serverless app in your React.js app. By setting them automatically with the ReactStaticSite construct, you won't need to hard code them in your frontend. ref: setting-serverless-environments-variables-in-a-react-app comments_id: setting-serverless-environments-variables-in-a-react-app/2430 +redirect_from: /chapters/setting-serverless-environments-variables-in-a-react-app.html --- A common question full-stack developers have is "How do I set the environment variables from my backend in my frontend app?". @@ -33,9 +34,9 @@ Here's what we want to happening when developing locally: 3. Then start our local React development environment. 4. It should automatically pick up the backend environment variables. -As an example, let's look at a really simple full-stack [SST app](/). It has a simple _Hello World_ API endpoint. And a React.js app. +As an example, let's look at a really simple full-stack [SST app](/){:target="_blank"}. It has a simple _Hello World_ API endpoint. And a React.js app. -```js +```typescript import * as sst from "@serverless-stack/resources"; export default class MyStack extends sst.Stack { @@ -69,7 +70,7 @@ export default class MyStack extends sst.Stack { Here we are using the [`ReactStaticSite`]({{ site.docs_url }}/constructs/ReactStaticSite) construct. It allows us to set React environment variables from our API. -```js +```typescript environment: { // Pass in the API endpoint to our app REACT_APP_API_URL: api.url, @@ -79,7 +80,7 @@ environment: { Now when we start our local development environment. ```bash -$ npm start +$ pnpm start ``` SST generates a file in the `.build/` directory with the environment that we configured. It looks something like this. @@ -96,10 +97,10 @@ SST generates a file in the `.build/` directory with the environment that we con ] ``` -On the React side, we'll now want to pick the environment variable up. To do this, we'll use a really simple CLI ([`@serverless-stack/static-site-env`](https://www.npmjs.com/package/@serverless-stack/static-site-env)) that reads from this file and sets it as a [build-time environment variable in React](https://create-react-app.dev/docs/adding-custom-environment-variables/). +On the React side, we'll now want to pick the environment variable up. To do this, we'll use a really simple CLI ([`@serverless-stack/static-site-env`](https://www.npmjs.com/package/@serverless-stack/static-site-env){:target="_blank"}) that reads from this file and sets it as a [build-time environment variable in React](https://create-react-app.dev/docs/adding-custom-environment-variables/){:target="_blank"}. ```bash -$ npm install @serverless-stack/static-site-env --save-dev +$ pnpm add --save-dev @serverless-stack/static-site-env ``` We can use the environment variable in our components using `process.env.REACT_APP_API_URL`. @@ -130,7 +131,7 @@ We can now wrap our start script with it. So if we start our React local environment: ```bash -$ npm run start +$ pnpm run start ``` It'll contain the environment variable that we had previously set in our serverless app! @@ -141,17 +142,17 @@ Next, let's look at what happens when we deploy our full-stack app. ## While Deploying -We need our React app to be deployed with our environment variables. SST uses [CDK]({% link _chapters/what-is-aws-cdk.md %}) internally, so the flow looks something like this. +We need our React app to be deployed with our environment variables. SST uses [CDK]({% link _chapters/what-is-aws-cdk.md %}){:target="_blank"} internally, so the flow looks something like this. 1. Deploy our API. 2. Build our React app. 3. Replace the environment variables in our React app. 4. Deploy our React app to S3 and CloudFront. -[SST](/) and the [`ReactStaticSite`]({{ site.docs_url }}/constructs/ReactStaticSite) construct do this automatically for you. +[SST](/){:target="_blank"} and the [`ReactStaticSite`]({{ site.docs_url }}/constructs/ReactStaticSite){:target="_blank"} construct do this automatically for you. ![Serverless environment variable set in a React app deployed to AWS](/assets/extra-credit/serverless-environment-variable-set-in-a-react-app-deployed-to-aws.png) And that's it! You now have a full-stack serverless app where the environment variables from your backend are automatically set in your React app. You don't need to hard code them anymore and they work in your local development environment as well! -For further details, check out our example on building a React.js app with SST: [**How to create a React.js app with serverless**]({% link _examples/how-to-create-a-reactjs-app-with-serverless.md %}) +For further details, check out our example on building a React.js app with SST: [**How to create a React.js app with serverless**]({% link _examples/how-to-create-a-reactjs-app-with-serverless.md %}){:target="_blank"}. diff --git a/_chapters/setting-up-your-project-on-netlify.md b/_archives/setting-up-your-project-on-netlify.md similarity index 97% rename from _chapters/setting-up-your-project-on-netlify.md rename to _archives/setting-up-your-project-on-netlify.md index 5f3a25b17..92f8e6624 100644 --- a/_chapters/setting-up-your-project-on-netlify.md +++ b/_archives/setting-up-your-project-on-netlify.md @@ -6,6 +6,7 @@ lang: en description: To host our React app on Netlify start by signing up for a free account and adding your Git repository. We are also adding a catch all `_redirects` file in our project root. ref: setting-up-your-project-on-netlify comments_id: setting-up-your-project-on-netlify/190 +redirect_from: /chapters/setting-up-your-project-on-netlify.html --- Now we are going to host our React app on [Netlify](https://www.netlify.com). For reference, here's our React app in a GitHub repo. diff --git a/_chapters/setup-a-custom-domain-with-ssl.md b/_archives/setup-a-custom-domain-with-ssl.md similarity index 97% rename from _chapters/setup-a-custom-domain-with-ssl.md rename to _archives/setup-a-custom-domain-with-ssl.md index a5b1aa21e..f96ede075 100644 --- a/_chapters/setup-a-custom-domain-with-ssl.md +++ b/_archives/setup-a-custom-domain-with-ssl.md @@ -5,8 +5,10 @@ date: 2017-02-08 02:00:00 lang: en description: We want to create a custom domain for our React.js app that is hosted on AWS. We also want to enable SSL or HTTPS. To do so, we are going to use Route 53 and request a certificate using the Certificate Manager service from AWS. comments_id: chapter-comments-set-up-ssl/1868 -redirect_from: /chapters/setup-ssl.html ref: setup-a-custom-domain-with-ssl +redirect_from: + - /chapters/setup-a-custom-domain-with-ssl.html + - /chapters/setup-ssl.html --- Now that our CloudFront Distribution has been created. Let's configure it with a custom domain and setup SSL. diff --git a/_chapters/setup-the-serverless-framework.md b/_archives/setup-the-serverless-framework.md similarity index 98% rename from _chapters/setup-the-serverless-framework.md rename to _archives/setup-the-serverless-framework.md index 8a27dc59d..ceee1449a 100644 --- a/_chapters/setup-the-serverless-framework.md +++ b/_archives/setup-the-serverless-framework.md @@ -6,6 +6,7 @@ lang: en ref: setup-the-serverless-framework description: To create our serverless backend API using AWS Lambda and API Gateway, we are going to use the Serverless Framework (https://serverless.com). Serverless Framework helps developers build and manage serverless apps on AWS and other cloud providers. We can install the Serverless Framework CLI from it’s NPM package and use it to create a new Serverless Framework project. comments_id: set-up-the-serverless-framework/145 +redirect_from: /chapters/setup-the-serverless-framework.html --- In this section we are going to use [Serverless Framework](https://github.com/serverless/serverless) to build our serverless app. It's very similar to [the notes app that we built using SST]({{ site.sst_demo_repo }}). With a couple of key differences: diff --git a/_chapters/setup-www-domain-redirect.md b/_archives/setup-www-domain-redirect.md similarity index 98% rename from _chapters/setup-www-domain-redirect.md rename to _archives/setup-www-domain-redirect.md index 3d8070984..fe34a16ab 100644 --- a/_chapters/setup-www-domain-redirect.md +++ b/_archives/setup-www-domain-redirect.md @@ -6,6 +6,7 @@ lang: en description: To create a www version of our domain for our React.js app on AWS we need to redirect it to our apex (or naked) domain. To create a domain that redirects we are going to create a new S3 Bucket and enable the “Redirect requests” option from the Static Website Hosting section in the AWS console. And we need to create a CloudFront Distribution for this and point our www domain to it. comments_id: set-up-www-domain-redirect/142 ref: setup-www-domain-redirect +redirect_from: /chapters/setup-www-domain-redirect.html --- There's plenty of debate over the www vs non-www domains and while both sides have merit; we'll go over how to set up another domain (in this case the www) and redirect it to our original. The reason we do a redirect is to tell the search engines that we only want one version of our domain to appear in the search results. If you prefer having the www domain as the default simply swap this step with the last one where we created a bare domain (non-www). diff --git a/_chapters/setup-your-domain-with-cloudfront.md b/_archives/setup-your-domain-with-cloudfront.md similarity index 98% rename from _chapters/setup-your-domain-with-cloudfront.md rename to _archives/setup-your-domain-with-cloudfront.md index e03faace9..437a7858a 100644 --- a/_chapters/setup-your-domain-with-cloudfront.md +++ b/_archives/setup-your-domain-with-cloudfront.md @@ -6,6 +6,7 @@ lang: en description: We will add our domain and its certificate to our CloudFront Distribution. We will point the domain to our CloudFront Distribution with an Alias Resource Record Set. We also need to create an AAAA Record Set to support IPv6. comments_id: set-up-your-domain-with-cloudfront/149 ref: set-up-your-domain-with-cloudfront +redirect_from: /chapters/setup-your-domain-with-cloudfront.html --- Now that we have our domain and a certificate to serve it over HTTPS, let's associate these with our CloudFront Distribution diff --git a/_chapters/share-an-api-endpoint-between-services.md b/_archives/share-an-api-endpoint-between-services.md similarity index 98% rename from _chapters/share-an-api-endpoint-between-services.md rename to _archives/share-an-api-endpoint-between-services.md index 10e58490f..ff5e0c67b 100644 --- a/_chapters/share-an-api-endpoint-between-services.md +++ b/_archives/share-an-api-endpoint-between-services.md @@ -2,9 +2,11 @@ layout: post title: Share an API Endpoint Between Services description: To share the same API Gateway domain across multiple services in serverless we need to "Export" the API Gateway Rest API Id and the API Gateway "RootResourceId" as a CloudFormation cross-stack reference. This will allow us to share the same API Gateway URL across Serverless projects. -redirect_from: /chapters/api-gateway-domains-across-services.html date: 2019-09-29 00:00:00 comments_id: api-gateway-domains-across-services/408 +redirect_from: + - /chapters/share-an-api-endpoint-between-services.html + - /chapters/api-gateway-domains-across-services.html --- In this chapter we will look at how to work with API Gateway across multiple services. A challenge that you run into when splitting your APIs into multiple services is sharing the same domain for them. You might recall that APIs that are created as a part of the Serverless Framework service get their own unique URL that looks something like: @@ -54,7 +56,7 @@ In our [serverless-stack-demo-ext-api]({{ site.backend_ext_api_github_repo }}) r Ref: ApiGatewayRestApi Export: Name: ${self:custom.stage}-ExtApiGatewayRestApiId - + ApiGatewayRestApiRootResourceId: Value: Fn::GetAtt: diff --git a/_chapters/share-code-between-services.md b/_archives/share-code-between-services.md similarity index 98% rename from _chapters/share-code-between-services.md rename to _archives/share-code-between-services.md index 0372522ab..fb57a0cce 100644 --- a/_chapters/share-code-between-services.md +++ b/_archives/share-code-between-services.md @@ -4,6 +4,7 @@ title: Share Code Between Services description: In this chapter we look at how to share common code and config between services in your serverless app. We'll look at how to structure the package.json and share config between multiple serverless.yml files. date: 2019-09-29 00:00:00 comments_id: share-code-between-services/1333 +redirect_from: /chapters/share-code-between-services.html --- In these next couple of chapters we'll look at how to organize all our business logic services (APIs) in the same repo. We'll start by attempting to answer the following questions: @@ -44,7 +45,7 @@ On the other hand, dependencies that are specific to a single service are instal This setup implies that when you are deploying your app through a CI; you’ll need to do an `npm install` twice. Once in the root level and once in a specific service. [Seed](https://seed.run/) does this automatically for you. -You can also use [Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) (and [Lerna](https://lerna.js.org/)) to manage the dependencies for your monorepo setup. We cover this setup in a separate chapter — [Using Lerna and Yarn Workspaces with Serverless]({% link _chapters/using-lerna-and-yarn-workspaces-with-serverless.md %}). +You can also use [Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) (and [Lerna](https://lerna.js.org/)) to manage the dependencies for your monorepo setup. We cover this setup in a separate chapter — [Using Lerna and Yarn Workspaces with Serverless]({% link _archives/using-lerna-and-yarn-workspaces-with-serverless.md %}). Usually, you might have to manually pick and choose the modules that need to be packaged with your Lambda function. Simply packaging all the dependencies will increase the code size of your Lambda function and this leads to longer cold start times. However, in our example we are using the `serverless-bundle` plugin that internally uses [Webpack](https://webpack.js.org/)’s tree shaking algorithm to only package the code that our Lambda function needs. diff --git a/_chapters/share-route-53-domains-across-aws-accounts.md b/_archives/share-route-53-domains-across-aws-accounts.md similarity index 97% rename from _chapters/share-route-53-domains-across-aws-accounts.md rename to _archives/share-route-53-domains-across-aws-accounts.md index f5ee2e554..f027e297a 100644 --- a/_chapters/share-route-53-domains-across-aws-accounts.md +++ b/_archives/share-route-53-domains-across-aws-accounts.md @@ -4,6 +4,7 @@ title: Share Route 53 Domains Across AWS Accounts description: In this chapter we look at how to delegate Route 53 domains across AWS accounts. This allows us to use the same custom domain for our serverless app across multiple environments. date: 2019-10-02 00:00:00 comments_id: share-route-53-domains-across-aws-accounts/1334 +redirect_from: /chapters/share-route-53-domains-across-aws-accounts.html --- Our notes app has an API Gateway endpoint. In this chapter, we are going to look at how to set up custom domains for each of our environments. Recall that our environments are split across multiple AWS accounts. @@ -103,6 +104,6 @@ Select the domain **dev.ext-api.sst.dev** and leave the subdomain empty. Then se Similarly, you might have to wait for up to 40 minutes. -Now we've delegated the `dev.api` subdomain of `notes-app.com` to our `Development` AWS account. We'll be configuring our app to use [these domains in a later chapter]({% link _chapters/share-route-53-domains-across-aws-accounts.md %}). +Now we've delegated the `dev.api` subdomain of `notes-app.com` to our `Development` AWS account. We'll be configuring our app to use [these domains in a later chapter]({% link _archives/share-route-53-domains-across-aws-accounts.md %}). Next, let's quickly look at how you'll be managing the cost and usage for your two AWS accounts. diff --git a/_chapters/stages-in-serverless-framework.md b/_archives/stages-in-serverless-framework.md similarity index 96% rename from _chapters/stages-in-serverless-framework.md rename to _archives/stages-in-serverless-framework.md index 990bb5568..10929c42e 100644 --- a/_chapters/stages-in-serverless-framework.md +++ b/_archives/stages-in-serverless-framework.md @@ -4,6 +4,7 @@ title: Stages in Serverless Framework description: Stages in Serverless Framework can be configured using the "stage:" setting in serverless.yml. You can also deploy to a stage using the "--stage" option in the "serverless deploy" command. To configure environment variables for the different stages, use the custom variables in the serverless.yml. date: 2018-04-06 00:00:00 comments_id: stages-in-serverless-framework/35 +redirect_from: /chapters/stages-in-serverless-framework.html --- Serverless Framework allows you to create stages for your project to deploy to. Stages are useful for creating environments for testing and development. Typically you create a staging environment that is an independent clone of your production environment. This allows you to test and ensure that the version of code that you are about to deploy is good to go. @@ -48,7 +49,7 @@ There are a couple of ways to set up stages for your project: - Separate AWS account for each stage - Just like how having each stage being separate APIs give us more flexibility to fine tune the IAM policy. We can take it a step further and create the API project in a different AWS account. Most companies don't keep their production infrastructure in the same account as their development infrastructure. This helps reduce any cases where developers accidentally edit/delete production resources. We go in to more detail on how to deploy to multiple AWS accounts using different AWS profiles in the [Configure Multiple AWS Profiles]({% link _chapters/configure-multiple-aws-profiles.md %}) chapter. + Just like how having each stage being separate APIs give us more flexibility to fine tune the IAM policy. We can take it a step further and create the API project in a different AWS account. Most companies don't keep their production infrastructure in the same account as their development infrastructure. This helps reduce any cases where developers accidentally edit/delete production resources. We go in to more detail on how to deploy to multiple AWS accounts using different AWS profiles in the [Configure Multiple AWS Profiles]({% link _archives/configure-multiple-aws-profiles.md %}) chapter. ### Deploying to a Stage @@ -70,7 +71,7 @@ $ serverless deploy --stage dev ### Stage Variables in Serverless Framework -Deploying to stages can be pretty simple but now let's look at how to configure our environment variables so that they work with our various stages. We went over the concept of environment variables in the chapter on [Serverless Environment Variables]({% link _chapters/serverless-environment-variables.md %}). Let's extend that to specify variables based on the stage we are deploying to. +Deploying to stages can be pretty simple but now let's look at how to configure our environment variables so that they work with our various stages. We went over the concept of environment variables in the chapter on [Serverless Environment Variables]({% link _archives/serverless-environment-variables.md %}). Let's extend that to specify variables based on the stage we are deploying to. Let's take a look at a sample `serverless.yml` below. diff --git a/_chapters/storing-secrets-in-serverless-apps.md b/_archives/storing-secrets-in-serverless-apps.md similarity index 97% rename from _chapters/storing-secrets-in-serverless-apps.md rename to _archives/storing-secrets-in-serverless-apps.md index 22626a5e2..e65bfca32 100644 --- a/_chapters/storing-secrets-in-serverless-apps.md +++ b/_archives/storing-secrets-in-serverless-apps.md @@ -4,6 +4,7 @@ title: Storing Secrets in Serverless Apps description: In this chapter we look at how to store secrets in your serverless app using AWS Systems Manager Parameter Store or SSM. Note that, SSM parameters should be decoded outside your Lambda functions. date: 2019-10-02 00:00:00 comments_id: storing-secrets-in-serverless-apps/1335 +redirect_from: /chapters/storing-secrets-in-serverless-apps.html --- The general idea behind secrets is to store them outside of your codebase — don't commit them to Git! And to make them available at runtime. There are many ways people tend to do this, some are less secure than others. This chapter is going to lay out the best practice for storing secrets and managing them across multiple environments. @@ -112,7 +113,7 @@ The above code reads the current stage from the environment variable `process.en - If the stage is `dev`, it exports `stageConfigs.dev`. - And if stage is `featureX`, it falls back to the dev config and exports `stageConfigs.dev`. -If you need a refresher on the structure of our config, refer to the [Manage environment related config]({% link _chapters/manage-environment-related-config.md %}). +If you need a refresher on the structure of our config, refer to the [Manage environment related config]({% link _archives/manage-environment-related-config.md %}). Now we can access the SSM value in our Lambda function. diff --git a/_chapters/structure-environments-across-aws-accounts.md b/_archives/structure-environments-across-aws-accounts.md similarity index 96% rename from _chapters/structure-environments-across-aws-accounts.md rename to _archives/structure-environments-across-aws-accounts.md index 8d12b95fe..2db068bec 100644 --- a/_chapters/structure-environments-across-aws-accounts.md +++ b/_archives/structure-environments-across-aws-accounts.md @@ -4,13 +4,14 @@ title: Structure Environments Across AWS Accounts description: It is recommended to use separate AWS accounts to manage environments in your serverless app. It helps you keep your environments separate while allowing you to better deal with resource limits. date: 2019-09-30 00:00:00 comments_id: structure-environments-across-aws-accounts/1336 +redirect_from: /chapters/structure-environments-across-aws-accounts.html --- The typical recommendation for teams is to deploy each of their environments to a separate AWS account. We find that this ends up being excessive for most teams. In our experience, what seems to work for many teams is: - One account for the Production environment. You want to apply very strict IAM access permissions to this account. - One account for **EACH** Staging environment. If you have multiple staging environment ie. `preprod`, `qa`, `uat`, etc, use a separate AWS account for each. You don't want to have multiple Staging environments share the same account because each Staging environment needs to mirror Production as closely as possible. -- One account for **ALL** Development environments. All feature and hotfix environments share the same AWS account. You will have many Development environments, many will be very short-lived. Creating a temporary AWS account for each environment and tearing it down after the change is merged into master is far too excessive. Especially when you need to push a quick hotfix. Also, as we mentioned in the [How to organize your services]({% link _chapters/organizing-serverless-projects.md %}) chapter, you have one repo for the infrastructure and one repo for the code. Each Development environment most likely does not need their own version of the infrastructure. Consider when the scenario where you push a hotfix. You can have multiple API environments all talk to the one Infrastructure environment. And having them all sit inside the same AWS account makes it easy for them to talk to each other without configuring cross-account IAM permissions. Of course, this is not a hard rule. If you have a major feature release, the release can have its own Infrastructure environment and the entire setup can be deployed to a separate AWS account. +- One account for **ALL** Development environments. All feature and hotfix environments share the same AWS account. You will have many Development environments, many will be very short-lived. Creating a temporary AWS account for each environment and tearing it down after the change is merged into master is far too excessive. Especially when you need to push a quick hotfix. Also, as we mentioned in the [How to organize your services]({% link _archives/organizing-serverless-projects.md %}) chapter, you have one repo for the infrastructure and one repo for the code. Each Development environment most likely does not need their own version of the infrastructure. Consider when the scenario where you push a hotfix. You can have multiple API environments all talk to the one Infrastructure environment. And having them all sit inside the same AWS account makes it easy for them to talk to each other without configuring cross-account IAM permissions. Of course, this is not a hard rule. If you have a major feature release, the release can have its own Infrastructure environment and the entire setup can be deployed to a separate AWS account. - One account for **EACH** Developer environment. Each developer on your team has their own playground account. At first glance, this might just seem like a whole lot of extra work and you might wonder if the added complexity is worth while. However, for teams this really helps protect your production environment. And we’ll go over the various ways it does so. diff --git a/_chapters/test-the-apis.md b/_archives/test-the-apis.md similarity index 92% rename from _chapters/test-the-apis.md rename to _archives/test-the-apis.md index 650722eee..63256d414 100644 --- a/_chapters/test-the-apis.md +++ b/_archives/test-the-apis.md @@ -6,6 +6,7 @@ lang: en ref: test-the-apis description: To test a serverless backend API secured using IAM and Cognito User Pool you need to follow a few steps. First, generate a user token by authenticating with the User Pool. Then use the user token to get a set of temporary IAM credentials using the Identity Pool. Finally, sign the API request using the IAM credentials using Signature Version 4 and make the request. To simplify this process we are going to use the “aws-api-gateway-cli-test” tool. comments_id: comments-for-test-the-apis/122 +redirect_from: /chapters/test-the-apis.html --- Now that we have our backend completely set up and secured, let's test the API we just deployed. @@ -28,10 +29,10 @@ The `npx` command is just a convenient way of running a NPM module without insta We need to pass in quite a bit of our info to complete the above steps. -- Use the username and password of the user created in the [Create a Cognito test user]({% link _chapters/create-a-cognito-test-user.md %}) chapter. -- Replace **YOUR_COGNITO_USER_POOL_ID**, **YOUR_COGNITO_APP_CLIENT_ID**, and **YOUR_COGNITO_REGION** with the values from the [Create a Cognito user pool]({% link _chapters/create-a-cognito-user-pool.md %}) chapter. In our case the region is `us-east-1`. -- Replace **YOUR_IDENTITY_POOL_ID** with the one from the [Create a Cognito identity pool]({% link _chapters/create-a-cognito-identity-pool.md %}) chapter. -- Use the **YOUR_API_GATEWAY_URL** and **YOUR_API_GATEWAY_REGION** with the ones from the [Deploy the APIs]({% link _chapters/deploy-the-apis.md %}) chapter. In our case the URL is `https://0f7jby961h.execute-api.us-east-1.amazonaws.com/prod` and the region is `us-east-1`. +- Use the username and password of the user created in the [Create a Cognito test user]({% link _archives/create-a-cognito-test-user.md %}) chapter. +- Replace **YOUR_COGNITO_USER_POOL_ID**, **YOUR_COGNITO_APP_CLIENT_ID**, and **YOUR_COGNITO_REGION** with the values from the [Create a Cognito user pool]({% link _archives/create-a-cognito-user-pool.md %}) chapter. In our case the region is `us-east-1`. +- Replace **YOUR_IDENTITY_POOL_ID** with the one from the [Create a Cognito identity pool]({% link _archives/create-a-cognito-identity-pool.md %}) chapter. +- Use the **YOUR_API_GATEWAY_URL** and **YOUR_API_GATEWAY_REGION** with the ones from the [Deploy the APIs]({% link _archives/deploy-the-apis.md %}) chapter. In our case the URL is `https://0f7jby961h.execute-api.us-east-1.amazonaws.com/prod` and the region is `us-east-1`. And run the following. @@ -93,9 +94,9 @@ We've now got a serverless API that's secure and handles user authentication. In - If you're on Windows and are using Git Bash, try adding a trailing slash to `YOUR_API_GATEWAY_URL` while removing the leading slash from `--path-template`. In our case, it would result in `--invoke-url https://ly55wbovq4.execute-api.us-east-1.amazonaws.com/prod/ --path-template notes`. You can follow the discussion on this [here](https://github.com/AnomalyInnovations/serverless-stack-com/issues/112#issuecomment-345996566). - There is a good chance that this error is happening even before our Lambda functions are invoked. So we can start by making sure our IAM Roles are configured properly for our Identity Pool. Follow the steps as detailed in our [Debugging serverless API Issues]({% link _chapters/debugging-serverless-api-issues.md %}#missing-iam-policy) chapter to ensure that your IAM Roles have the right set of permissions. + There is a good chance that this error is happening even before our Lambda functions are invoked. So we can start by making sure our IAM Roles are configured properly for our Identity Pool. Follow the steps as detailed in our [Debugging serverless API Issues]({% link _archives/debugging-serverless-api-issues.md %}#missing-iam-policy) chapter to ensure that your IAM Roles have the right set of permissions. - Next, you can [enable API Gateway logs]({% link _chapters/api-gateway-and-lambda-logs.md %}#enable-api-gateway-cloudwatch-logs) and follow [these instructions]({% link _chapters/api-gateway-and-lambda-logs.md %}#viewing-api-gateway-cloudwatch-logs) to read the requests that are being logged. This should give you a better idea of what is going on. + Next, you can [enable API Gateway logs]({% link _archives/api-gateway-and-lambda-logs.md %}#enable-api-gateway-cloudwatch-logs) and follow [these instructions]({% link _archives/api-gateway-and-lambda-logs.md %}#viewing-api-gateway-cloudwatch-logs) to read the requests that are being logged. This should give you a better idea of what is going on. Finally, make sure to look at the comment thread below. We've helped quite a few people with similar issues and it's very likely that somebody has run into a similar issue as you. @@ -111,7 +112,7 @@ We've now got a serverless API that's secure and handles user authentication. In }) ``` - And deploy it using `serverless deploy function -f create`. But we can't see this output when we make an HTTP request to it, since the console logs are not sent in our HTTP responses. We need to check the logs to see this. We have a [detailed chapter]({% link _chapters/api-gateway-and-lambda-logs.md %}#viewing-lambda-cloudwatch-logs) on working with API Gateway and Lambda logs and you can read about how to check your debug messages [here]({% link _chapters/api-gateway-and-lambda-logs.md %}#viewing-lambda-cloudwatch-logs). + And deploy it using `serverless deploy function -f create`. But we can't see this output when we make an HTTP request to it, since the console logs are not sent in our HTTP responses. We need to check the logs to see this. We have a [detailed chapter]({% link _archives/api-gateway-and-lambda-logs.md %}#viewing-lambda-cloudwatch-logs) on working with API Gateway and Lambda logs and you can read about how to check your debug messages [here]({% link _archives/api-gateway-and-lambda-logs.md %}#viewing-lambda-cloudwatch-logs). A common source of errors here is an improperly indented `serverless.yml`. Make sure to double-check the indenting in your `serverless.yml` by comparing it to the one from [this chapter](https://github.com/AnomalyInnovations/serverless-stack-demo-api/blob/master/serverless.yml). diff --git a/_chapters/test-the-billing-api.md b/_archives/test-the-billing-api.md similarity index 98% rename from _chapters/test-the-billing-api.md rename to _archives/test-the-billing-api.md index 2bf3897d0..e16e366f2 100644 --- a/_chapters/test-the-billing-api.md +++ b/_archives/test-the-billing-api.md @@ -6,6 +6,7 @@ lang: en description: To test our serverless Stripe billing API, we are going to mock the Lambda HTTP event. Pass in the Stripe test token and call the "serverless invoke local" command. ref: test-the-billing-api comments_id: test-the-billing-api/172 +redirect_from: /chapters/test-the-billing-api.html --- Now that we have our billing API all set up, let's do a quick test in our local environment. diff --git a/_chapters/tracing-serverless-apps-with-x-ray.md b/_archives/tracing-serverless-apps-with-x-ray.md similarity index 98% rename from _chapters/tracing-serverless-apps-with-x-ray.md rename to _archives/tracing-serverless-apps-with-x-ray.md index fc44dd93d..b914f6c33 100644 --- a/_chapters/tracing-serverless-apps-with-x-ray.md +++ b/_archives/tracing-serverless-apps-with-x-ray.md @@ -5,6 +5,7 @@ description: In this chapter we look at how to trace serverless apps using AWS X date: 2019-10-02 00:00:00 comments_id: tracing-serverless-apps-with-x-ray/1337 canonical_url: https://seed.run/blog/how-to-trace-serverless-apps-with-aws-x-ray.html +redirect_from: /chapters/tracing-serverless-apps-with-x-ray.html --- _This chapter is based on a blog post over on the [Seed blog](https://seed.run/blog/) — [www.seed.run/blog/how-to-trace-serverless-apps-with-aws-x-ray](https://seed.run/blog/how-to-trace-serverless-apps-with-aws-x-ray)._ diff --git a/_chapters/understanding-react-hooks.md b/_archives/understanding-react-hooks.md similarity index 99% rename from _chapters/understanding-react-hooks.md rename to _archives/understanding-react-hooks.md index 7c16e8930..cace13d3e 100644 --- a/_chapters/understanding-react-hooks.md +++ b/_archives/understanding-react-hooks.md @@ -4,6 +4,7 @@ title: Understanding React Hooks description: Transitioning from Class components in React to using Function components with React Hooks can be tricky. In this post we go over the React lifecycle and help you develop a simple mental model for understanding React Hooks. date: 2018-04-16 12:00:00 comments_id: understanding-react-hooks/1338 +redirect_from: /chapters/understanding-react-hooks.html --- React Hooks are a way for your function components to "hook" into React's lifecycle and state. They were introduced in [React 16.8.0](https://github.com/facebook/react/releases/tag/v16.8.0). Previously, only Class based components were able to use React's lifecycle and state. Aside from enabling Function components to do this, Hooks make it incredibly easy to reuse stateful logic between components. diff --git a/_chapters/using-aws-cdk-with-serverless-framework.md b/_archives/using-aws-cdk-with-serverless-framework.md similarity index 97% rename from _chapters/using-aws-cdk-with-serverless-framework.md rename to _archives/using-aws-cdk-with-serverless-framework.md index ee7cfae28..1e873c315 100644 --- a/_chapters/using-aws-cdk-with-serverless-framework.md +++ b/_archives/using-aws-cdk-with-serverless-framework.md @@ -4,16 +4,18 @@ title: Using AWS CDK with Serverless Framework date: 2020-09-14 00:00:00 lang: en description: To use AWS CDK and Serverless Framework together, you'll need to ensure that your CDK stacks are not deployed to multiple AWS accounts or environments. To fix this issue, we are going to use SST. -redirect_from: /chapters/connect-serverless-framework-and-cdk-with-sst.html ref: using-aws-cdk-with-serverless-framework comments_id: using-aws-cdk-with-serverless-framework/2101 +redirect_from: + - /chapters/using-aws-cdk-with-serverless-framework.html + - /chapters/connect-serverless-framework-and-cdk-with-sst.html --- -In this guide we've looked at two different ways of creating serverless applications; [using SST]({% link _chapters/create-an-sst-app.md %}) and [using Serverless Framework]({% link _chapters/setup-the-serverless-framework.md %}). But you can use the two of them together as well. +In this guide we've looked at two different ways of creating serverless applications; [using SST]({% link _chapters/create-an-sst-app.md %}) and [using Serverless Framework]({% link _archives/setup-the-serverless-framework.md %}). But you can use the two of them together as well. As in, you can create your infrastructure with SST and manage your Lambda functions and APIs with Serverless Framework. There are a couple of reasons why you might find yourself wanting to do this. -1. Your applications are currently built using Serverless Framework and you want to use [AWS CDK]({% link _chapters/what-is-aws-cdk.md %}) instead of [CloudFormation for your resources]({% link _chapters/configure-dynamodb-in-serverless.md %}). +1. Your applications are currently built using Serverless Framework and you want to use [AWS CDK]({% link _chapters/what-is-aws-cdk.md %}) instead of [CloudFormation for your resources]({% link _archives/configure-dynamodb-in-serverless.md %}). 2. Or, you are looking to [migrate from Serverless Framework to SST]({{ site.docs_url }}/migrating-from-serverless-framework). In this chapter we'll look at how we can use SST to define your infrastructure using CDK and connecting that to our Serverless Framework app. diff --git a/_chapters/using-cognito-to-add-authentication-to-a-serverless-app.md b/_archives/using-cognito-to-add-authentication-to-a-serverless-app.md similarity index 98% rename from _chapters/using-cognito-to-add-authentication-to-a-serverless-app.md rename to _archives/using-cognito-to-add-authentication-to-a-serverless-app.md index 4a1445b94..4fd13f5b5 100644 --- a/_chapters/using-cognito-to-add-authentication-to-a-serverless-app.md +++ b/_archives/using-cognito-to-add-authentication-to-a-serverless-app.md @@ -6,11 +6,12 @@ lang: en description: In this chapter we look at how to use Amazon Cognito to add authentication to a serverless API. We'll also look at how to connect to this API using AWS Amplify in a React.js app. ref: using-cognito-to-add-authentication-to-a-serverless-app comments_id: using-cognito-to-add-authentication-to-a-serverless-app/2434 +redirect_from: /chapters/using-cognito-to-add-authentication-to-a-serverless-app.html --- {% capture repo_url %}{{ site.sst_github_repo }}{{ site.sst_github_examples_prefix }}react-app-auth-cognito{% endcapture %} -In the [previous chapter]({% link _chapters/how-to-add-authentication-to-a-serverless-app.md %}) we looked at the basics of adding authentication to a serverless app. In this chapter we look at how to use [Amazon Cognito](https://aws.amazon.com/cognito/) to add authentication to a serverless API. We'll also look at how to connect to this API using [AWS Amplify](https://aws.amazon.com/amplify/) in a [React.js](https://reactjs.org) app. +In the [previous chapter]({% link _archives/how-to-add-authentication-to-a-serverless-app.md %}) we looked at the basics of adding authentication to a serverless app. In this chapter we look at how to use [Amazon Cognito](https://aws.amazon.com/cognito/) to add authentication to a serverless API. We'll also look at how to connect to this API using [AWS Amplify](https://aws.amazon.com/amplify/) in a [React.js](https://reactjs.org) app. ![Login with Cognito in React.js app](/assets/extra-auth/cognito/login-with-cognito-in-react-js-app.png) @@ -41,7 +42,7 @@ Let’s start with looking at how to add Cognito User Pool to our app. ### How to Add Cognito -In the [previous chapter]({% link _chapters/how-to-add-authentication-to-a-serverless-app.md %}) we talked about the various parts of Cognito ([User Pools and Identity Pools]({% link _chapters/cognito-user-pool-vs-identity-pool.md %})). +In the [previous chapter]({% link _archives/how-to-add-authentication-to-a-serverless-app.md %}) we talked about the various parts of Cognito ([User Pools and Identity Pools]({% link _archives/cognito-user-pool-vs-identity-pool.md %})). SST makes it easy to add these to your application. In [`stacks/MyStack.js`]({{ repo_url }}/stacks/MyStack.js) you'll notice. @@ -179,7 +180,7 @@ const site = new ReactStaticSite(this, "ReactSite", { }); ``` -The key here is that we are [setting the outputs from our backend as environment variables in React]({% link _chapters/setting-serverless-environments-variables-in-a-react-app.md %}). Specifically, we are passing in the: +The key here is that we are [setting the outputs from our backend as environment variables in React]({% link _archives/setting-serverless-environments-variables-in-a-react-app.md %}). Specifically, we are passing in the: 1. API endpoint 2. Region of our serverless app diff --git a/_chapters/using-lerna-and-yarn-workspaces-with-serverless.md b/_archives/using-lerna-and-yarn-workspaces-with-serverless.md similarity index 97% rename from _chapters/using-lerna-and-yarn-workspaces-with-serverless.md rename to _archives/using-lerna-and-yarn-workspaces-with-serverless.md index fc753c252..30d750cb0 100644 --- a/_chapters/using-lerna-and-yarn-workspaces-with-serverless.md +++ b/_archives/using-lerna-and-yarn-workspaces-with-serverless.md @@ -6,9 +6,10 @@ date: 2020-07-09 00:00:00 lang: en ref: using-lerna-and-yarn-workspace-with-serverless comments_id: using-lerna-and-yarn-workspaces-with-serverless/1958 +redirect_from: /chapters/using-lerna-and-yarn-workspaces-with-serverless.html --- -In the [Organizing Serverless Projects]({% link _chapters/organizing-serverless-projects.md %}) chapter we covered the standard monorepo setup. This included [how to share code between your services]({% link _chapters/share-code-between-services.md %}) and [how to deploy a Serverless app with interdependent services]({% link _chapters/deploy-a-serverless-app-with-dependencies.md %}). +In the [Organizing Serverless Projects]({% link _archives/organizing-serverless-projects.md %}) chapter we covered the standard monorepo setup. This included [how to share code between your services]({% link _archives/share-code-between-services.md %}) and [how to deploy a Serverless app with interdependent services]({% link _archives/deploy-a-serverless-app-with-dependencies.md %}). This setup works pretty well but as your team and project grows, you run into a new issue. You have some common code libraries that are used across multiple services. An update to these libraries would redeploy all your services. If your services were managed by separate folks on your team or by separate teams, this poses a problem. For any change made to the common code, would require all the other folks on your team to test or update their services. diff --git a/_chapters/what-is-an-arn.md b/_archives/what-is-an-arn.md similarity index 89% rename from _chapters/what-is-an-arn.md rename to _archives/what-is-an-arn.md index b6c1ca1d1..132c25cda 100644 --- a/_chapters/what-is-an-arn.md +++ b/_archives/what-is-an-arn.md @@ -6,9 +6,10 @@ lang: en ref: what-is-an-arn description: Amazon Resource Names (or ARNs) uniquely identify AWS resources. It is a globally unique identifier and follows a couple of pre-defined formats. ARNs are used primarily for communicating the reference to a resource and for defining IAM policies. comments_id: what-is-an-arn/34 +redirect_from: /chapters/what-is-an-arn.html --- -In the last chapter while we were looking at IAM policies we looked at how you can specify a resource using its ARN. Let's take a better look at what ARN is. +An important concept in IAM is the ARN. Here is the official definition: @@ -61,7 +62,7 @@ Finally, let's look at the common use cases for ARN. "Resource": "arn:aws:s3:::Hello-bucket/*" } ``` - + ARN is used to define which resource (S3 bucket in this case) the access is granted for. The wildcard `*` character is used here to match all resources inside the *Hello-bucket*. -Next let's configure our AWS CLI. We'll be using the info from the IAM user account we created previously. +Next, you can learn more about AWS AppSync. diff --git a/_chapters/what-is-aws-appsync.md b/_archives/what-is-aws-appsync.md similarity index 99% rename from _chapters/what-is-aws-appsync.md rename to _archives/what-is-aws-appsync.md index c4f16f1f4..77b2f910a 100644 --- a/_chapters/what-is-aws-appsync.md +++ b/_archives/what-is-aws-appsync.md @@ -6,6 +6,7 @@ lang: en description: AWS AppSync is a fully managed GraphQL service that can be used to build serverless backends. It can connect to data sources like DynamoDB, Lambda, RDS, etc. It also provides full support for subscriptions. ref: what-is-aws-appsync comments_id: what-is-aws-appsync/2432 +redirect_from: /chapters/what-is-aws-appsync.html --- [AWS AppSync](https://aws.amazon.com/appsync/) is a managed API service that you can use in your serverless backends. In this chapter we'll go over it in detail: @@ -258,7 +259,7 @@ In most cases, you will have to pull data intermittently with queries on demand To create a subscription, you’ll first need to create a schema type of subscription and add the AWS AppSync annotation `@aws_subscribe()` to it. -```ts +```typescript type Subscription { newTodo: Todo @aws_subscribe(mutations: ["newTodo"]) diff --git a/_chapters/what-is-iam.md b/_archives/what-is-iam.md similarity index 85% rename from _chapters/what-is-iam.md rename to _archives/what-is-iam.md index 6f974771d..1d6c556fe 100644 --- a/_chapters/what-is-iam.md +++ b/_archives/what-is-iam.md @@ -6,9 +6,10 @@ lang: en ref: what-is-iam description: AWS Identity and Access Management (or IAM) is a service that helps you securely control access to AWS resources. You can create IAM users and apply IAM policies to them. An IAM policy is a rule or set of rules defining the operations allowed/denied to be performed on a resource. An IAM role is very similar to a IAM user in that it is an identity with permissions but unlike a user it does not have any credentials tied to it. Instead an IAM role can be taken on by any user or resource that temporarily needs those permissions. comments_id: what-is-iam/23 +redirect_from: /chapters/what-is-iam.html --- -In the last chapter, we created an IAM user so that our AWS CLI can operate on our account without using the AWS Console. But the IAM concept is used very frequently when dealing with security for AWS services, so it is worth understanding it in a bit more detail. Unfortunately, IAM is made up of a lot of different parts and it can be very confusing for folks that first come across it. In this chapter we are going to take a look at IAM and its concepts in a bit more detail. +This Guide uses Amazon Identity and Access Management (IAM) to manage users. When we setup our AWS Account, we created our first IAM user so that our AWS CLI can operate on our account without using the AWS Console. The IAM concept serves a broader purpose. It is used very frequently when dealing with security for AWS services, so it is worth understanding it in a bit more detail. Unfortunately, IAM is made up of a lot of different parts and it can be very confusing for folks that first come across it. In this chapter we are going to take a look at IAM and its concepts in a bit more detail. Let's start with the official definition of IAM. @@ -69,7 +70,7 @@ And here is a policy that grants more granular access, only allowing retrieval o } ``` -We are using S3 resources in the above examples. But a policy looks similar for any of the AWS services. It just depends on the resource ARN for `Resource` property. An ARN is an identifier for a resource in AWS and we'll look at it in more detail in the next chapter. We also add the corresponding service actions and condition context keys in `Action` and `Condition` property. You can find all the available AWS Service actions and condition context keys for use in IAM Policies [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_actionsconditions.html). Aside from attaching a policy to a user, you can attach them to a role or a group. +We are using S3 resources in the above examples. But a policy looks similar for any of the AWS services. It just depends on the resource ARN for `Resource` property. An ARN is an identifier for a resource in AWS and we'll look at it in more detail in the next chapter. We also add the corresponding service actions and condition context keys in `Action` and `Condition` property. You can find all the available AWS Service actions and condition context keys for use in IAM Policies [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_actionsconditions.html){:target="_blank"}. Aside from attaching a policy to a user, you can attach them to a role or a group. ### What is an IAM Role @@ -83,7 +84,7 @@ Roles can be applied to users as well. In this case, the user is taking on the p ![IAM User with IAM Role diagram](/assets/iam/iam-user-as-iam-role.png) -You can also have a role tied to the ARN of a user from a different organization. This allows the external user to assume that role as a part of your organization. This is typically used when you have a third party service that is acting on your AWS Organization. You'll be asked to create a Cross-Account IAM Role and add the external user as a *Trust Relationship*. The *Trust Relationship* is telling AWS that the specified external user can assume this role. +You can also have a role tied to the ARN of a user from a different organization. This allows the external user to assume that role as a part of your organization. This is typically used when you have a third party service that is acting on your AWS Organization. You will be asked to create a Cross-Account IAM Role and add the external user as a *Trust Relationship*. The *Trust Relationship* is telling AWS that the specified external user can assume this role. ![External IAM User with IAM Role diagram](/assets/iam/external-user-with-iam-role.png) @@ -94,4 +95,4 @@ An IAM group is simply a collection of IAM users. You can use groups to specify ![Complete IAM Group, IAM Role, IAM User, and IAM Policy diagram](/assets/iam/complete-iam-concepts.png) -This should give you a quick idea of IAM and some of its concepts. We will be referring to a few of these in the coming chapters. Next let's quickly look at another AWS concept; the ARN. +This should give you a quick overview of IAM and some of its concepts. We will be referring to a few of these elsewhere in the guide. You should also review a related concept [AWS ARN]({% link _archives/what-is-an-arn.md %}){:target="_blank"}. diff --git a/_chapters/working-on-serverless-apps.md b/_archives/working-on-serverless-apps.md similarity index 66% rename from _chapters/working-on-serverless-apps.md rename to _archives/working-on-serverless-apps.md index 118d7494d..0d5cc6dfa 100644 --- a/_chapters/working-on-serverless-apps.md +++ b/_archives/working-on-serverless-apps.md @@ -4,6 +4,7 @@ title: Working on Serverless Apps description: In this section of the guide we look at the development workflow of a real world serverless app. date: 2019-10-07 00:00:00 comments_id: working-on-serverless-apps/1339 +redirect_from: /chapters/working-on-serverless-apps.html --- So to quickly recap, we've split our real world serverless app into two repos, [one creates our infrastructure resources]({{ site.backend_ext_resources_github_repo }}) and the [second creates our API services]({{ site.backend_ext_api_github_repo }}). @@ -12,11 +13,11 @@ We've also split our environments across two AWS accounts; `Development` and `Pr Here is roughly what we are going to be covering: -- [Developing your Lambda functions locally]({% link _chapters/invoke-lambda-functions-locally.md %}) -- [Invoking API Gateway endpoints locally]({% link _chapters/invoke-api-gateway-endpoints-locally.md %}) -- [Creating and working on feature environments]({% link _chapters/creating-feature-environments.md %}) -- [Creating a pull request environment]({% link _chapters/creating-pull-request-environments.md %}) -- [Promoting dev to production]({% link _chapters/promoting-to-production.md %}) -- [Rolling back]({% link _chapters/rollback-changes.md %}) +- [Developing your Lambda functions locally]({% link _archives/invoke-lambda-functions-locally.md %}) +- [Invoking API Gateway endpoints locally]({% link _archives/invoke-api-gateway-endpoints-locally.md %}) +- [Creating and working on feature environments]({% link _archives/creating-feature-environments.md %}) +- [Creating a pull request environment]({% link _archives/creating-pull-request-environments.md %}) +- [Promoting dev to production]({% link _archives/promoting-to-production.md %}) +- [Rolling back]({% link _archives/rollback-changes.md %}) Let's start with how you work locally on your Lambda functions. diff --git a/_chapters/working-with-3rd-party-apis.md b/_archives/working-with-3rd-party-apis.md similarity index 96% rename from _chapters/working-with-3rd-party-apis.md rename to _archives/working-with-3rd-party-apis.md index 20a322fa3..47078804c 100644 --- a/_chapters/working-with-3rd-party-apis.md +++ b/_archives/working-with-3rd-party-apis.md @@ -6,6 +6,7 @@ lang: en description: To learn how to use a 3rd party API in our AWS Lambda functions, we are going to create a billing API using Stripe. ref: working-with-3rd-party-apis comments_id: working-with-3rd-party-apis/168 +redirect_from: /chapters/working-with-3rd-party-apis.html --- So far we've created a basic CRUD (create, read, update, and delete) API. We are going to make a small addition to this by adding an endpoint that works with a 3rd party API. This section is also going to illustrate how to work with environment variables and how to accept credit card payments using Stripe. diff --git a/_chapters/wrapping-up-the-best-practices.md b/_archives/wrapping-up-the-best-practices.md similarity index 96% rename from _chapters/wrapping-up-the-best-practices.md rename to _archives/wrapping-up-the-best-practices.md index 0707acd7f..b074a9234 100644 --- a/_chapters/wrapping-up-the-best-practices.md +++ b/_archives/wrapping-up-the-best-practices.md @@ -5,6 +5,7 @@ lang: en description: Wrapping up the best practices section of the tutorial and going over the next steps. date: 2019-10-08 00:00:00 comments_id: wrapping-up-the-best-practices/1340 +redirect_from: /chapters/wrapping-up-the-best-practices.html --- Congratulations on completing this best practices section of the guide! diff --git a/_chapters/add-an-api-to-create-a-note.md b/_chapters/add-an-api-to-create-a-note.md index 8e464f0bc..f48672f55 100644 --- a/_chapters/add-an-api-to-create-a-note.md +++ b/_chapters/add-an-api-to-create-a-note.md @@ -12,15 +12,15 @@ Let's get started by creating the API for our notes app. We'll first add an API to create a note. This API will take the note object as the input and store it in the database with a new id. The note object will contain the `content` field (the content of the note) and an `attachment` field (the URL to the uploaded file). -### Creating a Stack +### Creating the API Stack -{%change%} Create a new file in `stacks/ApiStack.js` and add the following. +{%change%} Create a new file in `stacks/ApiStack.ts` and add the following. -```js -import { Api, use } from "sst/constructs"; +```typescript +import { Api, StackContext, use } from "sst/constructs"; import { StorageStack } from "./StorageStack"; -export function ApiStack({ stack, app }) { +export function ApiStack({ stack }: StackContext) { const { table } = use(StorageStack); // Create the API @@ -53,9 +53,9 @@ We are doing a couple of things of note here. - This new `ApiStack` references the `table` resource from the `StorageStack` that we created previously. -- We are creating an API using SST's [`Api`]({{ site.docs_url }}/constructs/Api) construct. +- We are creating an API using SST's [`Api`]({{ site.docs_url }}/constructs/Api){:target="_blank"} construct. -- We are [binding]({{ site.docs_url }}/resource-binding) our DynamoDB table to our API using the `bind` prop. This will allow our API to access our table. +- We are [binding]({{ site.docs_url }}/resource-binding){:target="_blank"} our DynamoDB table to our API using the `bind` prop. This will allow our API to access our table. - The first route we are adding to our API is the `POST /notes` route. It'll be used to create a note. @@ -65,48 +65,58 @@ We are doing a couple of things of note here. Let's add this new stack to the rest of our app. -{%change%} In `sst.config.ts`, import the API stack at the top. +{%change%} In `sst.config.ts`, replace the `stacks` function with. -```js -import { ApiStack } from "./stacks/ApiStack"; -``` - -{%change%} And, replace the `stacks` function with - - -```js +```typescript stacks(app) { app.stack(StorageStack).stack(ApiStack); }, ``` +{%change%} And, import the API stack at the top. +```typescript +import { ApiStack } from "./stacks/ApiStack"; +``` + + ### Add the Function Now let's add the function that'll be creating our note. -{%change%} Create a new file in `packages/functions/src/create.js` with the following. +{%change%} Create a new file in `packages/functions/src/create.ts` with the following. -```js -import * as uuid from "uuid"; +```typescript import AWS from "aws-sdk"; +import * as uuid from "uuid"; +import { APIGatewayProxyEvent } from "aws-lambda"; + import { Table } from "sst/node/table"; const dynamoDb = new AWS.DynamoDB.DocumentClient(); -export async function main(event) { - // Request body is passed in as a JSON encoded string in 'event.body' - const data = JSON.parse(event.body); +export async function main(event: APIGatewayProxyEvent) { + let data, params; - const params = { - TableName: Table.Notes.tableName, - Item: { - // The attributes of the item to be created - userId: "123", // The id of the author - noteId: uuid.v1(), // A unique uuid - content: data.content, // Parsed from request body - attachment: data.attachment, // Parsed from request body - createdAt: Date.now(), // Current Unix timestamp - }, - }; + // Request body is passed in as a JSON encoded string in 'event.body' + if (event.body) { + data = JSON.parse(event.body); + params = { + TableName: Table.Notes.tableName, + Item: { + // The attributes of the item to be created + userId: "123", // The id of the author + noteId: uuid.v1(), // A unique uuid + content: data.content, // Parsed from request body + attachment: data.attachment, // Parsed from request body + createdAt: Date.now(), // Current Unix timestamp + }, + }; + } else { + return { + statusCode: 404, + body: JSON.stringify({ error: true }), + }; + } try { await dynamoDb.put(params).promise(); @@ -115,10 +125,16 @@ export async function main(event) { statusCode: 200, body: JSON.stringify(params.Item), }; - } catch (e) { + } catch (error) { + let message; + if (error instanceof Error) { + message = error.message; + } else { + message = String(error); + } return { statusCode: 500, - body: JSON.stringify({ error: e.message }), + body: JSON.stringify({ error: message }), }; } } @@ -129,27 +145,38 @@ There are some helpful comments in the code but let's go over them quickly. - Parse the input from the `event.body`. This represents the HTTP request body. - It contains the contents of the note, as a string — `content`. - It also contains an `attachment`, if one exists. It's the filename of a file that will be uploaded to [our S3 bucket]({% link _chapters/create-an-s3-bucket-in-sst.md %}). -- We can access our DynamoDB table through `Table.Notes.tableName` from the `sst/node/table`, the [SST Node.js client]({{ site.docs_url }}/clients). Here `Notes` in `Table.Notes` is the name of our Table construct from the [Create a DynamoDB Table in SST]({% link _chapters/create-a-dynamodb-table-in-sst.md %}) chapter. By doing `bind: [table]` earlier in this chapter, we are allowing our API to access our table. +- We can access our DynamoDB table through `Table.Notes.tableName` from the `sst/node/table`, the [SST Node.js client]({{ site.docs_url }}/clients){:target="_blank"}. Here `Notes` in `Table.Notes` is the name of our Table construct from the [Create a DynamoDB Table in SST]({% link _chapters/create-a-dynamodb-table-in-sst.md %}) chapter. By doing `bind: [table]` earlier in this chapter, we are allowing our API to access our table. - The `userId` is the id for the author of the note. For now we are hardcoding it to `123`. Later we'll be setting this based on the authenticated user. - Make a call to DynamoDB to put a new object with a generated `noteId` and the current date as the `createdAt`. - And if the DynamoDB call fails then return an error with the HTTP status code `500`. -Let's go ahead and install the npm packages that we are using here. +Let's go ahead and install the packages that we are using here. + +{%change%} Navigate to the `functions` folder in your terminal. + +```bash +$ cd packages/functions +``` -{%change%} Run the following in the `packages/functions/` folder. +{%change%} Then, run the following **in the `packages/functions/` folder** (Not in root). ```bash -$ npm install aws-sdk uuid +$ pnpm add --save aws-sdk aws-lambda uuid +$ pnpm add --save-dev @types/uuid @types/aws-lambda ``` - **aws-sdk** allows us to talk to the various AWS services. +- **aws-lambda** - **uuid** generates unique ids. +- **@types/aws-lambda** & **@types/uuid** provides the TypeScript types. ### Deploy Our Changes -If you switch over to your terminal, you'll notice that your changes are being deployed. +If you switch over to your terminal, you will notice that your changes are being deployed. -Note that, you'll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `npx sst dev` will deploy your changes again. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} You should see that the new API stack has been deployed. @@ -166,44 +193,60 @@ It includes the API endpoint that we created. Now we are ready to test our new API. -Head over to the **API** tab in the [SST Console]({{ site.old_console_url }}) and check out the new API. +{%change%} Run the following in your terminal. -![SST Console API tab](/assets/part2/sst-console-api-tab.png) - -Here we can test our APIs. +```bash +curl -X POST \ +-H 'Content-Type: application/json' \ +-d '{"content":"Hello World","attachment":"hello.jpg"}' \ +/notes +``` -{%change%} Add the following request body to the **Body** field and hit **Send**. +Replace `` with the `ApiEndpoint` from the output above. For example, our command will look like: -```json -{"content":"Hello World","attachment":"hello.jpg"} +```bash +curl -X POST \ +-H 'Content-Type: application/json' \ +-d '{"content":"Hello World","attachment":"hello.jpg"}' \ +https://5bv7x0iuga.execute-api.us-east-1.amazonaws.com/notes ``` -You should see the create note API request being made. +Here we are making a POST request to our create note API. We are passing in the `content` and `attachment` as a JSON string. In this case the attachment is a made up file name. We haven’t uploaded anything to S3 yet. -![SST Console create note API request](/assets/part2/sst-console-create-note-api-request.png) +{%info%} +Make sure to keep your local environment, `sst dev` running in another window. +{%endinfo%} -Here we are making a POST request to our create note API. We are passing in the `content` and `attachment` as a JSON string. In this case the attachment is a made up file name. We haven't uploaded anything to S3 yet. +The response should look something like this. -If you head over to the **DynamoDB** tab, you'll see the new note. - -![SST Console new note](/assets/part2/sst-console-new-note.png) +```bash +{"userId":"123","noteId":"a46b7fe0-008d-11ec-a6d5-a1d39a077784","content":"Hello World","attachment":"hello.jpg","createdAt":1629336889054} +``` Make a note of the `noteId`. We are going to use this newly created note in the next chapter. ### Refactor Our Code -Before we move on to the next chapter, let's quickly refactor the code since we are going to be doing much of the same for all of our APIs. +Before we move on to the next chapter, let's refactor this code. Since we'll be doing the same basic actions for all of our APIs, it makes sense to [DRY our code](https://blog.boot.dev/clean-code/dry-code/){:target="_blank"} to create reusable shared behaviors for both application reliability and maintainability. -{%change%} Start by replacing our `create.js` with the following. +{%change%} Start by replacing our `create.ts` with the following. -```js -import { Table } from "sst/node/table"; +```typescript import * as uuid from "uuid"; +import { Table } from "sst/node/table"; import handler from "@notes/core/handler"; import dynamoDb from "@notes/core/dynamodb"; export const main = handler(async (event) => { - const data = JSON.parse(event.body); + let data = { + content: "", + attachment: "", + }; + + if (event.body != null) { + data = JSON.parse(event.body); + } + const params = { TableName: Table.Notes.tableName, Item: { @@ -218,7 +261,7 @@ export const main = handler(async (event) => { await dynamoDb.put(params); - return params.Item; + return JSON.stringify(params.Item); }); ``` @@ -231,59 +274,77 @@ This code doesn't work just yet but it shows you what we want to accomplish: Let's start by creating a `dynamodb` util that we can share across all our functions. We'll place this in the `packages/core` directory. This is where we'll be putting all our business logic. -{%change%} Create a `packages/core/src/dynamodb.js` file with: +{%change%} Create a `packages/core/src/dynamodb.ts` file with: -```js +```typescript import AWS from "aws-sdk"; +import { DocumentClient } from "aws-sdk/lib/dynamodb/document_client"; const client = new AWS.DynamoDB.DocumentClient(); export default { - get: (params) => client.get(params).promise(), - put: (params) => client.put(params).promise(), - query: (params) => client.query(params).promise(), - update: (params) => client.update(params).promise(), - delete: (params) => client.delete(params).promise(), + get: (params: DocumentClient.GetItemInput) => client.get(params).promise(), + put: (params: DocumentClient.PutItemInput) => client.put(params).promise(), + query: (params: DocumentClient.QueryInput) => client.query(params).promise(), + update: (params: DocumentClient.UpdateItemInput) => + client.update(params).promise(), + delete: (params: DocumentClient.DeleteItemInput) => + client.delete(params).promise(), }; ``` Here we are creating a convenience object that exposes the DynamoDB client methods that we are going to need in this guide. -{%change%} Also create a `packages/core/src/handler.js` file with the following. +{%change%} Also create a `packages/core/src/handler.ts` file with the following. + +```typescript +import { Context, APIGatewayProxyEvent } from "aws-lambda"; -```js -export default function handler(lambda) { - return async function (event, context) { +export default function handler( + lambda: (evt: APIGatewayProxyEvent, context: Context) => Promise +) { + return async function (event: APIGatewayProxyEvent, context: Context) { let body, statusCode; try { // Run the Lambda body = await lambda(event, context); statusCode = 200; - } catch (e) { - console.error(e); - body = { error: e.message }; + } catch (error) { statusCode = 500; + body = JSON.stringify({ + error: error instanceof Error ? error.message : String(error), + }); } // Return HTTP response return { + body, statusCode, - body: JSON.stringify(body), }; }; } ``` +{%change%} We are now using the aws-sdk to `core` as well. Run the following **in the `packages/core/` directory**. + + +```bash +$ pnpm add --save aws-sdk aws-lambda +$ pnpm add --save-dev @types/aws-lambda +``` + Let's go over this in detail. - We are creating a `handler` function that we'll use as a wrapper around our Lambda functions. - It takes our Lambda function as the argument. - We then run the Lambda function in a `try/catch` block. -- On success, we `JSON.stringify` the result and return it with a `200` status code. +- On success, we take the result and return it with a `200` status code. - If there is an error then we return the error message with a `500` status code. -It's **important to note** that the `handler.js` needs to be **imported before we import anything else**. This is because we'll be adding some error handling to it later that needs to be initialized when our Lambda function is first invoked. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} Next, we are going to add the API to get a note given its id. @@ -291,11 +352,15 @@ Next, we are going to add the API to get a note given its id. #### Common Issues +- path received type undefined + + Restarting `pnpm sst dev` should pick up the new type information and resolve this error. + - Response `statusCode: 500` - If you see a `statusCode: 500` response when you invoke your function, the error has been reported by our code in the `catch` block. You'll see a `console.error` is included in our `handler.js` code above. Incorporating logs like these can help give you insight on issues and how to resolve them. + If you see a `statusCode: 500` response when you invoke your function, the error has been reported by our code in the `catch` block. You'll see a `console.error` is included in our `handler.ts` code above. Incorporating logs like these can help give you insight on issues and how to resolve them. - ```js + ```typescript } catch (e) { // Prints the full error console.error(e); diff --git a/_chapters/add-an-api-to-delete-a-note.md b/_chapters/add-an-api-to-delete-a-note.md index ecedcc675..8bdafafab 100644 --- a/_chapters/add-an-api-to-delete-a-note.md +++ b/_chapters/add-an-api-to-delete-a-note.md @@ -12,9 +12,9 @@ Finally, we are going to create an API that allows a user to delete a given note ### Add the Function -{%change%} Create a new file in `packages/functions/src/delete.js` and paste the following. +{%change%} Create a new file in `packages/functions/src/delete.ts` and paste the following. -```js +```typescript import { Table } from "sst/node/table"; import handler from "@notes/core/handler"; import dynamoDb from "@notes/core/dynamodb"; @@ -22,16 +22,15 @@ import dynamoDb from "@notes/core/dynamodb"; export const main = handler(async (event) => { const params = { TableName: Table.Notes.tableName, - // 'Key' defines the partition key and sort key of the item to be removed Key: { userId: "123", // The id of the author - noteId: event.pathParameters.id, // The id of the note from the path + noteId: event?.pathParameters?.id, // The id of the note from the path }, }; await dynamoDb.delete(params); - return { status: true }; + return JSON.stringify({ status: true }); }); ``` @@ -41,19 +40,21 @@ This makes a DynamoDB `delete` call with the `userId` & `noteId` key to delete t Let's add a new route for the delete note API. -{%change%} Add the following below the `PUT /notes{id}` route in `stacks/ApiStack.js`. +{%change%} Add the following below the `PUT /notes{id}` route in `stacks/ApiStack.ts`. -```js +```typescript "DELETE /notes/{id}": "packages/functions/src/delete.main", ``` ### Deploy Our Changes -If you switch over to your terminal, you'll notice that your changes are being deployed. +If you switch over to your terminal, you will notice that your changes are being deployed. -Note that, you'll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `npx sst dev` will deploy your changes again. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} -You should see that the API stack is being updated. +You should see that the new API stack has been deployed. ```bash ✓ Deployed: @@ -68,17 +69,19 @@ Let's test the delete note API. In a [previous chapter]({% link _chapters/add-an-api-to-get-a-note.md %}) we tested our create note API. It should've returned the new note's id as the `noteId`. -In the **API** tab of the [SST Console]({{ site.old_console_url }}), select the `DELETE /notes/{id}` API. +{%change%} Run the following in your terminal. -{%change%} Set the `noteId` as the **id** and click **Send**. - -You should see the note being deleted in the response. +``` bash +$ curl -X DELETE https://5bv7x0iuga.execute-api.us-east-1.amazonaws.com/notes/ +``` -![SST Console delete note API request](/assets/part2/sst-console-delete-note-api-request.png) +Make sure to replace the id at the end of the URL with the `noteId` from before. -And the note should be removed from the DynamoDB Table as well. +Here we are making a DELETE request to the note that we want to delete. The response should look something like this. -![SST Console note removed in DynamoDB](/assets/part2/sst-console-note-removed-in-dynamodb.png) +``` json +{"status":true} +``` ### Commit the Changes diff --git a/_chapters/add-an-api-to-get-a-note.md b/_chapters/add-an-api-to-get-a-note.md index 406e07162..8553619e4 100644 --- a/_chapters/add-an-api-to-get-a-note.md +++ b/_chapters/add-an-api-to-get-a-note.md @@ -12,9 +12,9 @@ Now that we [created a note]({% link _chapters/add-an-api-to-create-a-note.md %} ### Add the Function -{%change%} Create a new file in `packages/functions/src/get.js` in your project root with the following: +{%change%} Create a new file in `packages/functions/src/get.ts` in your project root with the following: -```js +```typescript import { Table } from "sst/node/table"; import handler from "@notes/core/handler"; import dynamoDb from "@notes/core/dynamodb"; @@ -22,10 +22,11 @@ import dynamoDb from "@notes/core/dynamodb"; export const main = handler(async (event) => { const params = { TableName: Table.Notes.tableName, - // 'Key' defines the partition key and sort key of the item to be retrieved + // 'Key' defines the partition key and sort key of + // the item to be retrieved Key: { userId: "123", // The id of the author - noteId: event.pathParameters.id, // The id of the note from the path + noteId: event?.pathParameters?.id, // The id of the note from the path }, }; @@ -35,29 +36,31 @@ export const main = handler(async (event) => { } // Return the retrieved item - return result.Item; + return JSON.stringify(result.Item); }); ``` -This follows exactly the same structure as our previous `create.js` function. The major difference here is that we are doing a `dynamoDb.get(params)` to get a note object given the `userId` (still hardcoded) and `noteId` that's passed in through the request. +This follows exactly the same structure as our previous `create.ts` function. The major difference here is that we are doing a `dynamoDb.get(params)` to get a note object given the `userId` (still hardcoded) and `noteId` that's passed in through the request. ### Add the route Let's add a new route for the get note API. -{%change%} Add the following below the `POST /notes` route in `stacks/ApiStack.js`. +{%change%} Add the following below the `POST /notes` route in `stacks/ApiStack.ts`. -```js +```typescript "GET /notes/{id}": "packages/functions/src/get.main", ``` -### Deploy our changes +### Deploy Our Changes -If you switch over to your terminal, you'll notice that your changes are being deployed. +If you switch over to your terminal, you will notice that your changes are being deployed. -Note that, you'll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `npx sst dev` will deploy your changes again. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} -You should see that the API stack is being updated. +You should see that the new API stack has been deployed. ```bash ✓ Deployed: @@ -70,12 +73,20 @@ You should see that the API stack is being updated. Let's test the get notes API. In the [previous chapter]({% link _chapters/add-an-api-to-get-a-note.md %}) we tested our create note API. It should've returned the new note's id as the `noteId`. -Head back to the **API** tab in the [SST Console]({{ site.old_console_url }}) and select the `/notes/{id}` API. You might have to refresh your console. +{%change%} Run the following in your terminal. -{%change%} Set the `noteId` as the **id** and click **Send**. +``` bash +$ curl https://5bv7x0iuga.execute-api.us-east-1.amazonaws.com/notes/ +``` + +Make sure to replace the endpoint URL with your `ApiEndpoint` value and the at the end of the URL with the `noteId` that was created previously. -You should see the note being returned in the response. +Since we are making a simple GET request, we could also go to this URL directly in your browser. -![SST Console get note API request](/assets/part2/sst-console-get-note-api-request.png) +The response should look something like this. + +``` bash +{"attachment":"hello.jpg","content":"Hello World","createdAt":1629336889054,"noteId":"a46b7fe0-008d-11ec-a6d5-a1d39a077784","userId":"123"} +``` Next, let’s create an API to list all the notes a user has. diff --git a/_chapters/add-an-api-to-handle-billing.md b/_chapters/add-an-api-to-handle-billing.md index 2b151510d..a10a73157 100644 --- a/_chapters/add-an-api-to-handle-billing.md +++ b/_chapters/add-an-api-to-handle-billing.md @@ -12,26 +12,31 @@ Now let's get started with creating an API to handle billing. It's going to take ### Add a Billing Lambda -{%change%} Start by installing the Stripe NPM package. Run the following in the `packages/functions/` folder of our project. +Start by installing the Stripe npm package. + +{%change%} Run the following **in the `packages/functions/` directory** of our project. ```bash -$ npm install stripe +$ pnpm add --save stripe ``` -{%change%} Create a new file in `packages/functions/src/billing.js` with the following. +{%change%} Create a new file in `packages/functions/src/billing.ts` with the following. -```js +```typescript import Stripe from "stripe"; +import { Config } from "sst/node/config"; import handler from "@notes/core/handler"; import { calculateCost } from "@notes/core/cost"; export const main = handler(async (event) => { - const { storage, source } = JSON.parse(event.body); + const { storage, source } = JSON.parse(event.body || "{}"); const amount = calculateCost(storage); const description = "Scratch charge"; - // Load our secret key from the environment variables - const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); + // Load our secret key + const stripe = new Stripe(Config.STRIPE_SECRET_KEY, { + apiVersion: "2023-08-16", + }); await stripe.charges.create({ source, @@ -40,7 +45,7 @@ export const main = handler(async (event) => { currency: "usd", }); - return { status: true }; + return JSON.stringify({ status: true }); }); ``` @@ -50,20 +55,20 @@ Most of this is fairly straightforward but let's go over it quickly: - We are using a `calculateCost(storage)` function (that we are going to add soon) to figure out how much to charge a user based on the number of notes that are going to be stored. -- We create a new Stripe object using our Stripe Secret key. We are getting this from the environment variable that we configured in the [previous chapter]({% link _chapters/handling-secrets-in-sst.md %}). For newer versions of the Stripe SDK, you might've to pass in an API version. +- We create a new Stripe object using our Stripe Secret key. We are getting this from the environment variable that we configured in the [previous chapter]({% link _chapters/handling-secrets-in-sst.md %}). At the time of this guide's writing, we are using apiVersion `2022-11-15` but you can check the [Stripe documentation](https://stripe.com/docs/api/versioning){:target="_blank"} for the latest version. - Finally, we use the `stripe.charges.create` method to charge the user and respond to the request if everything went through successfully. -Note, if you are testing this from India, you'll need to add some shipping information as well. Check out the [details from our forums](https://discourse.sst.dev/t/test-the-billing-api/172/20). +Note, if you are testing this from India, you'll need to add some shipping information as well. Check out the [details from our forums](https://discourse.sst.dev/t/test-the-billing-api/172/20){:target="_blank"}. ### Add the Business Logic Now let's implement our `calculateCost` method. This is primarily our _business logic_. -{%change%} Create a `packages/core/src/cost.js` and add the following. +{%change%} Create a `packages/core/src/cost.ts` and add the following. -```js -export function calculateCost(storage) { +```typescript +export function calculateCost(storage: number) { const rate = storage <= 10 ? 4 : storage <= 100 ? 2 : 1; return rate * storage * 100; } @@ -77,19 +82,21 @@ Clearly, our serverless infrastructure might be cheap but our service isn't! Let's add a new route for our billing API. -{%change%} Add the following below the `DELETE /notes/{id}` route in `stacks/ApiStack.js`. +{%change%} Add the following below the `DELETE /notes/{id}` route in `stacks/ApiStack.ts`. -```js +```typescript "POST /billing": "packages/functions/src/billing.main", ``` ### Deploy Our Changes -If you switch over to your terminal, you'll notice that your changes are being deployed. +If you switch over to your terminal, you will notice that your changes are being deployed. -Note that, you'll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `npx sst dev` will deploy your changes again. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} -You should see that the API stack is being updated. +You should see that the new API stack has been deployed. ```bash ✓ Deployed: @@ -102,28 +109,31 @@ You should see that the API stack is being updated. Now that we have our billing API all set up, let's do a quick test in our local environment. -We'll be using the same CLI from [a few chapters ago]({% link _chapters/secure-our-serverless-apis.md %}). +We'll be using the same CLI from [a few chapters ago]({% link _chapters/secure-our-serverless-apis.md %}){:target="_blank"}. {%change%} Run the following in your terminal. ```bash -$ npx aws-api-gateway-cli-test \ +$ pnpm dlx aws-api-gateway-cli-test \ --username='admin@example.com' \ --password='Passw0rd!' \ ---user-pool-id='USER_POOL_ID' \ ---app-client-id='USER_POOL_CLIENT_ID' \ ---cognito-region='COGNITO_REGION' \ ---identity-pool-id='IDENTITY_POOL_ID' \ ---invoke-url='API_ENDPOINT' \ ---api-gateway-region='API_REGION' \ +--user-pool-id='' \ +--app-client-id='' \ +--cognito-region='' \ +--identity-pool-id='' \ +--invoke-url='' \ +--api-gateway-region='' \ --path-template='/billing' \ --method='POST' \ --body='{"source":"tok_visa","storage":21}' ``` +{%note%} +Make sure to replace the `USER_POOL_ID`, `USER_POOL_CLIENT_ID`, `COGNITO_REGION`, `IDENTITY_POOL_ID`, `API_ENDPOINT`, and `API_REGION` with the [same values we used a couple of chapters ago]({% link _chapters/secure-our-serverless-apis.md %}){:target="_blank"}. -Make sure to replace the `USER_POOL_ID`, `USER_POOL_CLIENT_ID`, `COGNITO_REGION`, `IDENTITY_POOL_ID`, `API_ENDPOINT`, and `API_REGION` with the [same values we used a couple of chapters ago]({% link _chapters/secure-our-serverless-apis.md %}). +If you have the previous request, update the `path-template` and `body` with the new values. +{%endnote%} -Here we are testing with a Stripe test token called `tok_visa` and with `21` as the number of notes we want to store. You can read more about the Stripe test cards and tokens in the [Stripe API Docs here](https://stripe.com/docs/testing#cards). +Here we are testing with a Stripe test token called `tok_visa` and with `21` as the number of notes we want to store. You can read more about the Stripe test cards and tokens in the [Stripe API Docs here](https://stripe.com/docs/testing#cards){:target="_blank"}. If the command is successful, the response will look similar to this. diff --git a/_chapters/add-an-api-to-list-all-the-notes.md b/_chapters/add-an-api-to-list-all-the-notes.md index 539a7f2c8..61ac5d4fd 100644 --- a/_chapters/add-an-api-to-list-all-the-notes.md +++ b/_chapters/add-an-api-to-list-all-the-notes.md @@ -12,14 +12,14 @@ Now we are going to add an API that returns a list of all the notes a user has. ### Add the Function -{%change%} Create a new file in `packages/functions/src/list.js` with the following. +{%change%} Create a new file in `packages/functions/src/list.ts` with the following. -```js +```typescript import { Table } from "sst/node/table"; import handler from "@notes/core/handler"; import dynamoDb from "@notes/core/dynamodb"; -export const main = handler(async () => { +export const main = handler(async (event) => { const params = { TableName: Table.Notes.tableName, // 'KeyConditionExpression' defines the condition for the query @@ -36,29 +36,31 @@ export const main = handler(async () => { const result = await dynamoDb.query(params); // Return the matching list of items in response body - return result.Items; + return JSON.stringify(result.Items); }); ``` -This is pretty much the same as our `get.js` except we use a condition to only return the items that have the same `userId` as the one we are passing in. In our case, it's still hardcoded to `123`. +This is pretty much the same as our `get.ts` except we use a condition to only return the items that have the same `userId` as the one we are passing in. In our case, it's still hardcoded to `123`. ### Add the Route Let's add the route for this new endpoint. -{%change%} Add the following above the `POST /notes` route in `stacks/ApiStack.js`. +{%change%} Add the following above the `POST /notes` route in `stacks/ApiStack.ts`. -```js +```typescript "GET /notes": "packages/functions/src/list.main", ``` ### Deploy Our Changes -If you switch over to your terminal, you'll notice that your changes are being deployed. +If you switch over to your terminal, you will notice that your changes are being deployed. -Note that, you'll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `npx sst dev` will deploy your changes again. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} -You should see that the API stack is being updated. +You should see that the new API stack has been deployed. ```bash ✓ Deployed: @@ -69,13 +71,23 @@ You should see that the API stack is being updated. ### Test the API -Let's test the list all notes API. Head to the **API** tab of the [SST Console]({{ site.old_console_url }}). +Let's test list all notes API. -{%change%} Select the `/notes` API and click **Send**. +{%change%} Run the following in your terminal. -You should see the notes being returned in the response. +``` bash +$ curl https://5bv7x0iuga.execute-api.us-east-1.amazonaws.com/notes +``` + +Again, replacing the example URL with your `ApiEndpoint` value. + +Since we are making a simple GET request, we could also go to this URL directly in your browser. -![SST Console list notes API request](/assets/part2/sst-console-list-notes-api-request.png) +The response should look something like this. + +``` json +[{"attachment":"hello.jpg","content":"Hello World","createdAt":1629336889054,"noteId":"a46b7fe0-008d-11ec-a6d5-a1d39a077784","userId":"123"}] +``` Note that, we are getting an array of notes. Instead of a single note. diff --git a/_chapters/add-an-api-to-update-a-note.md b/_chapters/add-an-api-to-update-a-note.md index 00f49f94e..92e90d49f 100644 --- a/_chapters/add-an-api-to-update-a-note.md +++ b/_chapters/add-an-api-to-update-a-note.md @@ -12,21 +12,22 @@ Now let's create an API that allows a user to update a note with a new note obje ### Add the Function -{%change%} Create a new file in `packages/functions/src/update.js` and paste the following. +{%change%} Create a new file in `packages/functions/src/update.ts` and paste the following. -```js +```typescript import { Table } from "sst/node/table"; import handler from "@notes/core/handler"; import dynamoDb from "@notes/core/dynamodb"; export const main = handler(async (event) => { - const data = JSON.parse(event.body); + const data = JSON.parse(event.body || "{}"); + const params = { TableName: Table.Notes.tableName, - // 'Key' defines the partition key and sort key of the item to be updated Key: { + // The attributes of the item to be created userId: "123", // The id of the author - noteId: event.pathParameters.id, // The id of the note from the path + noteId: event?.pathParameters?.id, // The id of the note from the path }, // 'UpdateExpression' defines the attributes to be updated // 'ExpressionAttributeValues' defines the value in the update expression @@ -43,29 +44,31 @@ export const main = handler(async (event) => { await dynamoDb.update(params); - return { status: true }; + return JSON.stringify({ status: true }); }); ``` -This should look similar to the `create.js` function. Here we make an `update` DynamoDB call with the new `content` and `attachment` values in the `params`. +This should look similar to the `create.ts` function combined. Here we make an `update` DynamoDB call with the new `content` and `attachment` values in the `params`. ### Add the Route Let's add a new route for the get note API. -{%change%} Add the following below the `GET /notes/{id}` route in `stacks/ApiStack.js`. +{%change%} Add the following below the `GET /notes/{id}` route in `stacks/ApiStack.ts`. -```js +```typescript "PUT /notes/{id}": "packages/functions/src/update.main", ``` ### Deploy Our Changes -If you switch over to your terminal, you'll notice that your changes are being deployed. +If you switch over to your terminal, you will notice that your changes are being deployed. -Note that, you'll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `npx sst dev` will deploy your changes again. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} -You should see that the API stack is being updated. +You should see that the new API stack has been deployed. ```bash ✓ Deployed: @@ -78,16 +81,23 @@ You should see that the API stack is being updated. Now we are ready to test the new API. In [an earlier chapter]({% link _chapters/add-an-api-to-get-a-note.md %}) we tested our create note API. It should've returned the new note's id as the `noteId`. -Head to the **API** tab in the [SST Console]({{ site.old_console_url }}) and select the `PUT /notes/{id}` API. - -{%change%} Set the `noteId` as the **id** and in the **Body** tab set the following as the request body. Then hit **Send**. +{%change%} Run the following in your terminal. -```json -{"content":"New World","attachment":"new.jpg"} +``` bash +$ curl -X PUT \ +-H 'Content-Type: application/json' \ +-d '{"content":"New World","attachment":"new.jpg"}' \ +https://5bv7x0iuga.execute-api.us-east-1.amazonaws.com/notes/ ``` -You should see the note being updated in the response. +Make sure to replace the id at the end of the URL with the `noteId` from before. + +Here we are making a PUT request to a note that we want to update. We are passing in the new `content` and `attachment` as a JSON string. -![SST Console update note API request](/assets/part2/sst-console-update-note-api-request.png) +The response should look something like this. + +``` json +{"status":true} +``` Next we are going to add the API to delete a note given its id. diff --git a/_chapters/add-app-favicons.md b/_chapters/add-app-favicons.md index eacf87909..49f6c26c6 100644 --- a/_chapters/add-app-favicons.md +++ b/_chapters/add-app-favicons.md @@ -4,19 +4,19 @@ title: Add App Favicons date: 2017-01-07 00:00:00 lang: en ref: add-app-favicons -description: To generate app icons and favicons for our React.js app we will use the Realfavicongenerator.net service. This will replace the default favicon that Create React App comes with. +description: To generate app icons and favicons for our React.js app we will use the Realfavicongenerator.net service. This will replace the default favicon that the Vite React template comes with. comments_id: add-app-favicons/155 --- -Create React App generates a simple favicon for our app and places it in `public/favicon.ico` of our app. However, getting the favicon to work on all browsers and mobile platforms requires a little more work. There are quite a few different requirements and dimensions. And this gives us a good opportunity to learn how to include files in the `public/` directory of our app. +Vite generates a simple favicon for our app and places it in `public/vite.svg` of our app. However, getting the favicon to work on all browsers and mobile platforms requires a little more work. There are quite a few different requirements and dimensions. And this gives us a good opportunity to learn how to include files in the `public/` directory of our app. For our example, we are going to start with a simple image and generate the various versions from it. -**Right-click to download** the following image. Or head over to this link to download it — [{{ '/assets/scratch-icon.png' | absolute_url }}]({{ '/assets/scratch-icon.png' | absolute_url }}) +**Right-click to download** the following image. Or head over to this link to download it — [{{ '/assets/scratch-icon.png' | absolute_url }}]({{ '/assets/scratch-icon.png' | absolute_url }}){:target="_blank"} App Icon -To ensure that our icon works for most of our targeted platforms we'll use a service called the [**Favicon Generator**](http://realfavicongenerator.net). +To ensure that our icon works for most of our targeted platforms we'll use a service called the [**Favicon Generator**](http://realfavicongenerator.net){:target="_blank"}. Click **Select your Favicon picture** to upload our icon. @@ -34,15 +34,11 @@ This should generate your favicon package and the accompanying code. Let's remove the old icons files. -**Note that, moving forward we'll be working exclusively in the `frontend/` directory.** +{%note%} +We'll be working exclusively **in the `packages/frontend/` directory** for the rest of the frontend part of the guide. +{%endnote%} -{%change%} Run the following from our `frontend/` directory. - -```bash -$ rm public/logo192.png public/logo512.png public/favicon.ico -``` - -{%change%} Then replace the contents of `public/manifest.json` with the following: +{%change%} Then replace the contents of `public/site.webmanifest` with the following: ```json { @@ -67,46 +63,24 @@ $ rm public/logo192.png public/logo512.png public/favicon.ico } ``` -To include a file from the `public/` directory in your HTML, Create React App needs the `%PUBLIC_URL%` prefix. - -{%change%} Add this to your `public/index.html`. +{%change%} Add this to the `` in your `public/index.html`. ```html - - - - + + + + + + - ``` -{%change%} And **remove** the following lines that reference the original favicon and theme color. +{%change%} And **remove** the following lines that reference the original favicon. ```html - - - - + ``` -Finally head over to your browser and try the `/favicon-32x32.png` path to ensure that the files were added correctly. +Finally head over to your browser and add `/favicon-32x32.png` to the base URL path to ensure that the files were added correctly. Next we are going to look into setting up custom fonts in our app. diff --git a/_chapters/add-stripe-keys-to-config.md b/_chapters/add-stripe-keys-to-config.md index e59424d86..8c9320300 100644 --- a/_chapters/add-stripe-keys-to-config.md +++ b/_chapters/add-stripe-keys-to-config.md @@ -10,36 +10,32 @@ comments_id: add-stripe-keys-to-config/185 Back in the [Setup a Stripe account]({% link _chapters/setup-a-stripe-account.md %}) chapter, we had two keys in the Stripe console. The **Secret key** that we used in the backend and the **Publishable key**. The **Publishable key** is meant to be used in the frontend. -{%change%} Add the following line below the `const config = {` line in your `src/config.js`. +{%change%} Add the following line below the `const config = {` line in your `src/config.ts`. -```txt -STRIPE_KEY: "YOUR_STRIPE_PUBLIC_KEY", +```typescript +STRIPE_KEY: "", ``` Make sure to replace, `YOUR_STRIPE_PUBLIC_KEY` with the **Publishable key** from the [Setup a Stripe account]({% link _chapters/setup-a-stripe-account.md %}) chapter. Let's also add the Stripe.js packages -{%change%} Run the following in the `frontend/` directory and **not** in your project root. +{%change%} Run the following **in the `packages/frontend/` directory**. ```bash -$ npm install @stripe/stripe-js +$ pnpm add --save @stripe/stripe-js ``` And load the Stripe config in our settings page. -{%change%} Add the following at top of the `Settings` component in `src/containers/Settings.js` above the `billUser()` function. +{%change%} Add the following **below the imports** in `src/containers/Settings.tsx`. + +```typescript +import { loadStripe } from "@stripe/stripe-js"; -```js const stripePromise = loadStripe(config.STRIPE_KEY); ``` This loads the Stripe object from Stripe.js with the Stripe key when our settings page loads. We'll be using this in the coming chapters. -{%change%} We'll also import this function at the top. - -```js -import { loadStripe } from "@stripe/stripe-js"; -``` - Next, we'll build our billing form. diff --git a/_chapters/add-the-create-note-page.md b/_chapters/add-the-create-note-page.md index 4ea51573c..210f284a6 100644 --- a/_chapters/add-the-create-note-page.md +++ b/_chapters/add-the-create-note-page.md @@ -14,19 +14,18 @@ First we are going to create the form for a note. It'll take some content and a ### Add the Container -{%change%} Create a new file `src/containers/NewNote.js` and add the following. +{%change%} Create a new file `src/containers/NewNote.tsx` and add the following. -```jsx -import React, { useRef, useState } from "react"; +```tsx +import React, {useRef, useState} from "react"; import Form from "react-bootstrap/Form"; -import { useNavigate } from "react-router-dom"; +import {useNavigate} from "react-router-dom"; import LoaderButton from "../components/LoaderButton"; -import { onError } from "../lib/errorLib"; import config from "../config"; import "./NewNote.css"; export default function NewNote() { - const file = useRef(null); + const file = useRef(null); const nav = useNavigate(); const [content, setContent] = useState(""); const [isLoading, setIsLoading] = useState(false); @@ -35,11 +34,12 @@ export default function NewNote() { return content.length > 0; } - function handleFileChange(event) { - file.current = event.target.files[0]; + function handleFileChange(event: React.ChangeEvent) { + if ( event.currentTarget.files === null ) return + file.current = event.currentTarget.files[0]; } - async function handleSubmit(event) { + async function handleSubmit(event: React.FormEvent) { event.preventDefault(); if (file.current && file.current.size > config.MAX_ATTACHMENT_SIZE) { @@ -69,9 +69,8 @@ export default function NewNote() { ); } + ``` Everything is fairly standard here, except for the file input. Our form elements so far have been [controlled components](https://facebook.github.io/react/docs/forms.html), as in their value is directly controlled by the state of the component. However, in the case of the file input we want the browser to handle this state. So instead of `useState` we'll use the `useRef` hook. The main difference between the two is that `useRef` does not cause the component to re-render. It simply tells React to store a value for us so that we can use it later. We can set/get the current value of a ref by using its `current` property. Just as we do when the user selects a file. -```js +```typescript file.current = event.target.files[0]; ``` Currently, our `handleSubmit` does not do a whole lot other than limiting the file size of our attachment. We are going to define this in our config. -{%change%} So add the following to our `src/config.js` below the `const config = {` line. +{%change%} So add the following to our `src/config.ts` below the `const config = {` line. -```txt +```typescript +// Frontend config MAX_ATTACHMENT_SIZE: 5000000, ``` @@ -109,20 +110,20 @@ MAX_ATTACHMENT_SIZE: 5000000, ### Add the Route -{%change%} Finally, add our container as a route in `src/Routes.js` below our signup route. +{%change%} Finally, add our container as a route in `src/Routes.tsx` below our signup route. -```jsx +```tsx } /> ``` {%change%} And include our component in the header. -```js -import NewNote from "./containers/NewNote"; +```tsx +import NewNote from "./containers/NewNote.tsx"; ``` -Now if we switch to our browser and navigate `http://localhost:3000/notes/new` we should see our newly created form. Try adding some content, uploading a file, and hitting submit to see it in action. +Now if we switch to our browser and navigate `/notes/new` we should see our newly created form. Try adding some content, uploading a file, and hitting submit to see it in action. -![New note page added screenshot](/assets/new-note-page-added.png) +![New note page added screenshot](/assets/part2/new-note-page-added.png) Next, let's get into connecting this form to our API. diff --git a/_chapters/add-the-session-to-the-state.md b/_chapters/add-the-session-to-the-state.md index 68d3bff91..4afd2922c 100644 --- a/_chapters/add-the-session-to-the-state.md +++ b/_chapters/add-the-session-to-the-state.md @@ -3,7 +3,7 @@ layout: post title: Add the Session to the State date: 2017-01-15 00:00:00 lang: en -comments_id: add-the-session-to-the-state +ref: add-the-session-to-the-state redirect_from: /chapters/add-the-user-token-to-the-state.html description: We need to add the user session to the state of our App component in our React.js app. We are going to use React context through the useContext hook to store it and pass it to all our child components. comments_id: add-the-session-to-the-state/136 @@ -15,35 +15,29 @@ To complete the login process we would need to update the app state with the ses First we'll start by updating the application state by setting that the user is logged in. We might be tempted to store this in the `Login` container, but since we are going to use this in a lot of other places, it makes sense to lift up the state. The most logical place to do this will be in our `App` component. -To save the user's login state, let's include the `useState` hook in `src/App.js`. +To save the user's login state, let's include the `useState` hook in `src/App.tsx`. -{%change%} Replace the `React` import: - -```js -import React from "react"; -``` - -{%change%} With the following: +{%change%} Add the following to the top of our `App` component function. -```js -import React, { useState } from "react"; +```tsx +const [isAuthenticated, userHasAuthenticated] = useState(false); ``` -{%change%} Add the following to the top of our `App` component function. +{%change%} Then import it. -```js -const [isAuthenticated, userHasAuthenticated] = useState(false); +```tsx +import { useState } from "react"; ``` This initializes the `isAuthenticated` state variable to `false`, as in the user is not logged in. And calling `userHasAuthenticated` updates it. But for the `Login` container to call this method we need to pass a reference of this method to it. ### Store the Session in the Context -We are going to have to pass the session related info to all of our containers. This is going to be tedious if we pass it in as a prop, since we'll have to do that manually for each component. Instead let's use [React Context](https://reactjs.org/docs/context.html) for this. +We are going to have to pass the session related info to all of our containers. This is going to be tedious if we pass it in as a prop, since we'll have to do that manually for each component. Instead let's use [React Context](https://reactjs.org/docs/context.html){:target="_blank"} for this. We'll create a context for our entire app that all of our containers will use. -{%change%} Create a `src/lib/` directory in the `frontend/` React directory. +{%change%} Create a `src/lib/` directory in the `packages/frontend/` React directory. ```bash $ mkdir src/lib/ @@ -51,12 +45,20 @@ $ mkdir src/lib/ We'll use this to store all our common code. -{%change%} Add the following to `src/lib/contextLib.js`. +{%change%} Add the following file with the content below `src/lib/contextLib.ts`. + +```typescript +import { createContext, useContext } from "react"; -```js -import { useContext, createContext } from "react"; +export interface AppContextType { + isAuthenticated: boolean; + userHasAuthenticated: React.Dispatch>; +} -export const AppContext = createContext(null); +export const AppContext = createContext({ + isAuthenticated: false, + userHasAuthenticated: useAppContext, +}); export function useAppContext() { return useContext(AppContext); @@ -70,17 +72,17 @@ This really simple bit of code is creating and exporting two things: If you are not sure how Contexts work, don't worry, it'll make more sense once we use it. -{%change%} Import our new app context in the header of `src/App.js`. +{%change%} Import our new app context in the header of `src/App.tsx`. -```js -import { AppContext } from "./lib/contextLib"; +```tsx +import { AppContext, AppContextType } from "./lib/contextLib"; ``` Now to add our session to the context and to pass it to our containers: -{%change%} Wrap our `Routes` component in the `return` statement of `src/App.js`. +{%change%} Wrap our `Routes` component in the `return` statement of `src/App.tsx`. -```jsx +```tsx ``` @@ -88,17 +90,20 @@ Now to add our session to the context and to pass it to our containers: {% raw %} -```jsx - +```tsx + + ``` {% endraw %} React Context's are made up of two parts. The first is the Provider. This is telling React that all the child components inside the Context Provider should be able to access what we put in it. In this case we are putting in the following object: -```js +```tsx { isAuthenticated, userHasAuthenticated; } @@ -106,33 +111,33 @@ React Context's are made up of two parts. The first is the Provider. This is tel ### Use the Context to Update the State -The second part of the Context API is the consumer. We'll add that to the Login container: +The second part of the Context API is the consumer. We'll add that to the Login container, `src/containers/Login.tsx`. -{%change%} Start by importing it in the header of `src/containers/Login.js`. +{%change%} Include the hook by adding it below the `export default function Login() {` line. -```js -import { useAppContext } from "../lib/contextLib"; +```tsx +const { userHasAuthenticated } = useAppContext(); ``` -{%change%} Include the hook by adding it below the `export default function Login() {` line. +{%change%} And import it in the header of `src/containers/Login.tsx`. -```js -const { userHasAuthenticated } = useAppContext(); +```tsx +import { useAppContext } from "../lib/contextLib"; ``` This is telling React that we want to use our app context here and that we want to be able to use the `userHasAuthenticated` function. -{%change%} Finally, replace the `alert('Logged in');` line with the following in `src/containers/Login.js`. +{%change%} Finally, replace the `alert('Logged in');` line with the following in `src/containers/Login.tsx`. -```js +```tsx userHasAuthenticated(true); ``` ### Create a Logout Button -We can now use this to display a Logout button once the user logs in. Find the following in our `src/App.js`. +We can now use this to display a Logout button once the user logs in. Find the following in our `src/App.tsx`. -```jsx +```tsx Signup @@ -143,7 +148,7 @@ We can now use this to display a Logout button once the user logs in. Find the f {%change%} And replace it with this: -```jsx +```tsx {isAuthenticated ? ( Logout ) : ( @@ -158,17 +163,17 @@ We can now use this to display a Logout button once the user logs in. Find the f )} ``` -The `<>` or [Fragment component](https://reactjs.org/docs/fragments.html) can be thought of as a placeholder component. We need this because in the case the user is not logged in, we want to render two links. To do this we would need to wrap it inside a single component, like a `div`. But by using the Fragment component it tells React that the two links are inside this component but we don't want to render any extra HTML. +The `<>` or Fragment component can be thought of as a placeholder component. We need this because in the case the user is not logged in, we want to render two links. To do this we would need to wrap it inside a single component, like a `div`. But by using the Fragment component it tells React that the two links are inside this component but we don't want to render any extra HTML. -{%change%} And add this `handleLogout` method to `src/App.js` above the `return` statement as well. +{%change%} And add this `handleLogout` method to `src/App.tsx` above the `return` statement as well. -```js +```tsx function handleLogout() { userHasAuthenticated(false); } ``` -Now head over to your browser and try logging in with the admin credentials we created in the [Secure Our Serverless APIs]({% link _chapters/secure-our-serverless-apis.md %}) chapter. You should see the Logout button appear right away. +Now head over to your browser and try logging in with the admin credentials we created in the [Secure Our Serverless APIs]({% link _chapters/secure-our-serverless-apis.md %}){:target="_blank"} chapter. You should see the Logout button appear right away. ![Login state updated screenshot](/assets/login-state-updated.png) diff --git a/_chapters/adding-auth-to-our-serverless-app.md b/_chapters/adding-auth-to-our-serverless-app.md index 8f0dd9178..3e4338764 100644 --- a/_chapters/adding-auth-to-our-serverless-app.md +++ b/_chapters/adding-auth-to-our-serverless-app.md @@ -11,23 +11,23 @@ ref: adding-auth-to-our-serverless-app comments_id: adding-auth-to-our-serverless-app/2457 --- -So far we've created the [DynamoDB table]({% link _chapters/create-a-dynamodb-table-in-sst.md %}), [S3 bucket]({% link _chapters/create-an-s3-bucket-in-sst.md %}), and [API]({% link _chapters/add-an-api-to-create-a-note.md %}) parts of our serverless backend. Now let's add auth into the mix. As we talked about in the [previous chapter]({% link _chapters/auth-in-serverless-apps.md %}), we are going to use [Cognito User Pool](https://aws.amazon.com/cognito/) to manage user sign ups and logins. While we are going to use [Cognito Identity Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html) to manage which resources our users have access to. +So far we've created the [DynamoDB table]({% link _chapters/create-a-dynamodb-table-in-sst.md %}), [S3 bucket]({% link _chapters/create-an-s3-bucket-in-sst.md %}), and [API]({% link _chapters/add-an-api-to-create-a-note.md %}) parts of our serverless backend. Now let's add auth into the mix. As we talked about in the [previous chapter]({% link _chapters/auth-in-serverless-apps.md %}), we are going to use [Cognito User Pool](https://aws.amazon.com/cognito/){:target="_blank"} to manage user sign ups and logins. While we are going to use [Cognito Identity Pool](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html){:target="_blank"} to manage which resources our users have access to. -Setting this all up can be pretty complicated in CDK. SST has a simple [`Auth`]({{ site.docs_url }}/constructs/Auth) construct to help with this. +Setting this all up can be pretty complicated in CDK. SST has a simple [`Auth`]({{ site.docs_url }}/constructs/Auth){:target="_blank"} construct to help with this. ### Create a Stack -{%change%} Add the following to a new file in `stacks/AuthStack.js`. +{%change%} Add the following to a new file in `stacks/AuthStack.ts`. -```js +```typescript +import { ApiStack } from "./ApiStack"; import * as iam from "aws-cdk-lib/aws-iam"; -import { Cognito, use } from "sst/constructs"; import { StorageStack } from "./StorageStack"; -import { ApiStack } from "./ApiStack"; +import { Cognito, StackContext, use } from "sst/constructs"; -export function AuthStack({ stack, app }) { - const { bucket } = use(StorageStack); +export function AuthStack({ stack, app }: StackContext) { const { api } = use(ApiStack); + const { bucket } = use(StorageStack); // Create a Cognito User Pool and Identity Pool const auth = new Cognito(stack, "Auth", { @@ -51,8 +51,8 @@ export function AuthStack({ stack, app }) { stack.addOutputs({ Region: app.region, UserPoolId: auth.userPoolId, - IdentityPoolId: auth.cognitoIdentityPoolId, UserPoolClientId: auth.userPoolClientId, + IdentityPoolId: auth.cognitoIdentityPoolId, }); // Return the auth resource @@ -62,9 +62,9 @@ export function AuthStack({ stack, app }) { } ``` -Let's quickly go over what we are doing here. +Let's go over what we are doing here. -- We are creating a new stack for our auth infrastructure. We don't need to create a separate stack but we are using it as an example to show how to work with multiple stacks. +- We are creating a new stack for our auth infrastructure. While we don't need to create a separate stack, we are using it as an example to show how to work with multiple stacks. - The `Auth` construct creates a Cognito User Pool for us. We are using the `login` prop to state that we want our users to login with their email. @@ -74,15 +74,17 @@ Let's quickly go over what we are doing here. - And we want them to access our S3 bucket. We'll look at this in detail below. -- Finally, we output the ids of the auth resources that've been created and returning the auth resource so that other stacks can access this resource. +- Finally, we output the ids of the auth resources that have been created and returning the auth resource so that other stacks can access this resource. -Note, learn more about sharing resources between stacks [here](https://docs.sst.dev/constructs/Stack#sharing-resources-between-stacks). +{% info %} +Learn more about how to [share resources between stacks]({{ site.docs_url }}/constructs/Stack#sharing-resources-between-stacks){:target="_blank"}. +{% endinfo %} ### Securing Access to Uploaded Files We are creating a specific IAM policy to secure the files our users will upload to our S3 bucket. -```js +```typescript // Policy granting access to a specific folder in the bucket new iam.PolicyStatement({ actions: ["s3:*"], @@ -101,19 +103,18 @@ One other thing to note is that, the federated identity id is a UUID that is ass ### Add to the App -Let's add this stack to our app. +Let's add this stack to our config in `sst.config.ts`. -{%change%} Replace the `stacks` function in `sst.config.ts` with this. +{%change%} Replace the `stacks` function with this line that adds the `AuthStack` into our list of stacks. -```js +```typescript stacks(app) { app.stack(StorageStack).stack(ApiStack).stack(AuthStack); }, ``` +{%change%} And import the new stack at the top of the file. -{%change%} Also, import the new stack at the top. - -```js +```typescript import { AuthStack } from "./stacks/AuthStack"; ``` @@ -121,21 +122,23 @@ import { AuthStack } from "./stacks/AuthStack"; We also need to enable authentication in our API. -{%change%} Add the following above the `function: {` line in `stacks/ApiStack.js`. +{%change%} Add the following prop into the `defaults` options above the `function: {` line in `stacks/ApiStack.ts`. -```js +```typescript authorizer: "iam", ``` This tells our API that we want to use `AWS_IAM` across all our routes. -### Deploy the App +### Deploy Our Changes -If you switch over to your terminal, you'll notice that your changes are being deployed. +If you switch over to your terminal, you will notice that your changes are being deployed. -Note that, you'll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `npx sst dev` will deploy your changes again. +{%caution%} +You’ll need to have `sst dev` running for this to happen. If you had previously stopped it, then running `pnpm sst dev` will deploy your changes again. +{%endcaution%} -You should see something like this at the end of the deploy process. +You should see that the new Auth stack is being deployed. ```bash ✓ Deployed: @@ -149,20 +152,39 @@ You should see something like this at the end of the deploy process. UserPoolId: us-east-1_TYEz7XP7P ``` -You'll also see our new User Pool if you head over to the **Cognito** tab in the [SST Console]({{ site.old_console_url }}). - -![SST Console Cognito tab](/assets/part2/sst-console-cognito-tab.png) +Let's create a test user so that we can test our API. ### Create a Test User -Let's create a test user so that we can test our API. Click the **Create User** button. +We'll use AWS CLI to sign up a user with their email and password. + +{%change%} In your terminal, run. + +``` bash +$ aws cognito-idp sign-up \ + --region \ + --client-id \ + --username admin@example.com \ + --password Passw0rd! +``` -{%change%} Fill in `admin@example.com` as the **Email** and `Passw0rd!` as the **Password**, then hit **Create**. +Make sure to replace `COGNITO_REGION` and `USER_POOL_CLIENT_ID` with the `Region` and `UserPoolClientId` from above. -![SST Console Cognito create new user](/assets/part2/sst-console-cognito-create-new-user.png) +Now we need to verify this email. For now we'll do this via an administrator command. + +{%change%} In your terminal, run. + +``` bash +$ aws cognito-idp admin-confirm-sign-up \ + --region \ + --user-pool-id \ + --username admin@example.com +``` -This should create a new user. +Replace the `COGNITO_REGION` and `USER_POOL_ID` with the `Region` and `UserPoolId` from above. -![SST Console Cognito new user](/assets/part2/sst-console-cognito-new-user.png) +{%caution%} +The first command uses the `USER_POOL_CLIENT_ID` while the second command uses the `USER_POOL_ID`. Make sure to replace it with the right values. +{%endcaution%} Now that the auth infrastructure and a test user has been created, let's use them to secure our APIs and test them. diff --git a/_chapters/adding-links-in-the-navbar.md b/_chapters/adding-links-in-the-navbar.md index 342690b6f..93a850364 100644 --- a/_chapters/adding-links-in-the-navbar.md +++ b/_chapters/adding-links-in-the-navbar.md @@ -10,16 +10,14 @@ comments_id: adding-links-in-the-navbar/141 Now that we have our first route set up, let's add a couple of links to the navbar of our app. These will direct users to login or signup for our app when they first visit it. -{%change%} Replace the `App` function component in `src/App.js` with the following. +{%change%} Replace the `App` function component in `src/App.tsx` with the following. -```jsx +```tsx function App() { return (

- - Scratch - + Scratch