<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: sudokar</title>
    <description>The latest articles on DEV Community by sudokar (@sudokar).</description>
    <link>https://dev.to/sudokar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F534385%2F8b9f629f-074c-461f-b1d6-94f74d177787.jpg</url>
      <title>DEV Community: sudokar</title>
      <link>https://dev.to/sudokar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sudokar"/>
    <language>en</language>
    <item>
      <title>Seamless CJS and ESM: Building Dual-Format Packages with Nx</title>
      <dc:creator>sudokar</dc:creator>
      <pubDate>Sun, 26 Oct 2025 20:25:54 +0000</pubDate>
      <link>https://dev.to/sudokar/seamless-cjs-and-esm-building-dual-format-packages-with-nx-lda</link>
      <guid>https://dev.to/sudokar/seamless-cjs-and-esm-building-dual-format-packages-with-nx-lda</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Intro
&lt;/h2&gt;

&lt;p&gt;The JavaScript module ecosystem is in transition. ESM (ECMAScript Modules) has become the modern standard, but CommonJS (CJS) remains deeply entrenched in the ecosystem.&lt;/p&gt;

&lt;p&gt;ECMAScript Modules (ESM), introduced in ES6 (2015), is the standardised JavaScript module system designed to work seamlessly across both browsers and servers, unifying how JavaScript handles modularity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core advantages of ESM:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Enables better performance and optimisation&lt;/strong&gt; through asynchronous loading and built-in tree-shaking via static analysis, allowing browsers and bundlers to eliminate unused code automatically.&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Provides universal compatibility&lt;/strong&gt; with native browser support (no bundlers required) while also working seamlessly on the server-side, making it suitable for both environments.&lt;/li&gt;
&lt;li&gt;✨ &lt;strong&gt;Offers modern developer features&lt;/strong&gt;, including top-level await support and dynamic imports in both module systems, giving developers more flexibility in how they structure and load their code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a library author, it’s important to support both legacy CommonJS projects and modern ESM projects. The solution is to publish a single npm package compatible with both module formats. This guide demonstrates how to build and publish a universal NPM package using the &lt;a href="https://nx.dev" rel="noopener noreferrer"&gt;Nx Dev Toolkit&lt;/a&gt; and &lt;a href="https://rollupjs.org" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Setting up Nx Workspace
&lt;/h2&gt;

&lt;p&gt;Let’s start! Setting up an Nx workspace is the first step to building a supercharged, scalable project structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a publishable library (with rollup bundler)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bunx create-nx-workspace@22 dual-modules-workspace

cd dual-modules-workspace
bunx nx g @nx/js:lib packages/my-library --name my-library --importPath @dual-modules-workspace/my-library --bundler rollup --linter eslint --publishable --unitTestRunner vitest --setParserOptionsProject --useProjectJson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
packages
└── my-library
    ├── eslint.config.mjs
    ├── package.json
    ├── project.json
    ├── README.md
    ├── rollup.config.cjs
    ├── src/
    ├── tsconfig.json
    ├── tsconfig.lib.json
    ├── tsconfig.spec.json
    └── vite.config.ts
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔧 Setting up configurations
&lt;/h2&gt;

&lt;p&gt;Now that the workspace is ready, it’s time to fine-tune the engine—let’s tweak the configs so everything runs smoothly and efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rollup configuration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// packages/my-library/rollup.config.cjs

const { withNx } = require('@nx/rollup/with-nx');

module.exports = withNx(
  {
    main: './src/index.ts',
    outputPath: './dist',
    tsConfig: './tsconfig.lib.json',
    compiler: 'swc',
    format: ['esm', 'cjs'],
  },
  {
    output: {
      preserveModules: true, // Better tree-shaking
      preserveModulesRoot: '.', // Preserve module structure in output
    },
  }
);

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Package configuration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// packages/my-library/package.json

{
  "name": "@dual-modules-workspace/my-library",
  "version": "0.0.1",
  "main": "./dist/index.cjs.js", // generated esm to cjs
  "module": "./dist/index.esm.js",
  "types": "./dist/index.d.ts", // generated index.esm.d.ts to index.d.ts
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "types": "./dist/index.d.ts", // generated index.esm.d.ts to index.d.ts
      "require": "./dist/index.cjs.js", // Support for common js
      "import": "./dist/index.esm.js",
      "default": "./dist/index.esm.js" // Keep ESM as default
    }
  },
  "sideEffects": false // // Better tree-shaking
  ...
}

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;exports&lt;/code&gt; field in &lt;strong&gt;package.json&lt;/strong&gt; routes &lt;code&gt;import&lt;/code&gt; to ESM and &lt;code&gt;require&lt;/code&gt; to CJS automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typescript declarations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Optionally, we can disable source maps added to the package by setting the property &lt;code&gt;sourceMap&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; in the tsconfig.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// tsconfig.base.json (not tsconfig.json)

{
  "compilerOptions": {
  ...
  "sourceMap": false,
  ...
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📦 Build
&lt;/h2&gt;

&lt;p&gt;It’s showtime! Let’s run &lt;code&gt;nx build&lt;/code&gt; and watch out library come to life after all that setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bun nx build my-library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packages/my-library/dist/
├── index.cjs.js
├── index.d.ts
├── index.esm.js
└── src
    ├── index.d.ts
    └── lib
        ├── my-library.cjs.js
        ├── my-library.d.ts
        └── my-library.esm.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚢 Publishing
&lt;/h2&gt;

&lt;p&gt;Nx comes with a built-in command to seamlessly publish our packages to the npm registry. It also natively supports semantic versioning with conventional commits, making release management smooth and predictable. To get started, follow the step-by-step guide &lt;a href="https://nx.dev/docs/guides/nx-release/release-npm-packages" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and explore additional release configuration options &lt;a href="https://nx.dev/docs/guides/nx-release" rel="noopener noreferrer"&gt;here&lt;/a&gt; to tailor the setup to our project’s needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎉 Outro
&lt;/h2&gt;

&lt;p&gt;Building universal npm packages with Nx and Rollup becomes straightforward once you grasp the key concepts. By setting up dual-format outputs, organising our package.json exports correctly, and validating our builds with the right tools, we ensure our library runs smoothly across the entire JavaScript ecosystem.&lt;/p&gt;

&lt;p&gt;With our library now ready to serve both legacy and modern JavaScript consumers, you’ve crafted a package that’s versatile, robust, and future-proof.&lt;/p&gt;

&lt;p&gt;If you found this guide helpful, don’t forget to leave a reaction or comment on this post — it helps me understand what content resonates with you and motivates me to create more like this!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Building Well-Architected AWS AppSync GraphQL APIs</title>
      <dc:creator>sudokar</dc:creator>
      <pubDate>Mon, 14 Oct 2024 08:10:51 +0000</pubDate>
      <link>https://dev.to/sudokar/building-well-architected-aws-appsync-graphql-apis-1a6c</link>
      <guid>https://dev.to/sudokar/building-well-architected-aws-appsync-graphql-apis-1a6c</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AWS AppSync&lt;/a&gt; is a fully managed serverless service from AWS for building scalable and resilient GraphQL APIs.&lt;/p&gt;

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

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/welcome.html" rel="noopener noreferrer"&gt;AWS Well-Architected Framework&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/serverless-applications-lens/welcome.html" rel="noopener noreferrer"&gt;Serverless Application Lens&lt;/a&gt; offer best practices for building serverless applications in the cloud. This post will show how to apply these practices when creating GraphQL APIs with AWS AppSync, aligned with the Well-Architected pillars.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Excellence
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Focuses on running and monitoring systems to deliver business value, and continuously improving processes and procedures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Enable Observability
&lt;/h4&gt;

&lt;p&gt;    Observability is the ability to understand the internal state of a system based on the data it generates, which includes logs, metrics, and traces. Together, these components enable you to monitor, troubleshoot, and optimise system behaviour efficiently.&lt;/p&gt;

&lt;p&gt;    AWS AppSync has built-in integration with &lt;a href="https://aws.amazon.com/cloudwatch/" rel="noopener noreferrer"&gt;AWS Cloudwatch&lt;/a&gt; for logs and metrics and &lt;a href="https://aws.amazon.com/xray/" rel="noopener noreferrer"&gt;X-Ray&lt;/a&gt; for tracing.&lt;/p&gt;

&lt;p&gt;    AppSync supports two types of logging: Request-level logging and Field-level logging. Field-level logging supports five logging levels (&lt;code&gt;None&lt;/code&gt;, &lt;code&gt;Error&lt;/code&gt;, &lt;code&gt;Info&lt;/code&gt;, &lt;code&gt;Debug&lt;/code&gt; and &lt;code&gt;All&lt;/code&gt;), providing flexibility in the amount of detail captured for each GraphQL query.&lt;/p&gt;

&lt;p&gt;    AppSync captures a variety of default and enhanced metrics with detailed data points to monitor API performance and usage.&lt;/p&gt;

&lt;p&gt;    AppSync uses X-Ray to capture traces, allowing us to track the lifecycle of API requests, measure execution times for detailed performance analysis, and identify errors for easier troubleshooting.&lt;/p&gt;

&lt;p&gt;    When using Lambda resolvers, implement logging and manually instrument traces using AWS X-Ray SDK. Utilise libraries like &lt;a href="https://github.com/aws-powertools/" rel="noopener noreferrer"&gt;Powertools for lambda&lt;/a&gt; and &lt;a href="https://middy.js.org/" rel="noopener noreferrer"&gt;Middy&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Use Infrastructure as Code (IaC)
&lt;/h4&gt;

&lt;p&gt;    For prototyping and deploying, consider using Infrastructure as Code (IaC) to enable tracking of changes using version control and releases. Cloudformation, Terraform, AWS SAM, and AWS CDK are a few examples.&lt;/p&gt;

&lt;p&gt;    If you are using AWS CDK, this &lt;a href="https://constructs.dev/packages/cdk-appsync-typescript-resolver/v/0.0.27?lang=typescript" rel="noopener noreferrer"&gt;CDK construct&lt;/a&gt; simplifies writing AppSync resolvers using Typescript.&lt;/p&gt;

&lt;h4&gt;
  
  
  Test, Test, Test
&lt;/h4&gt;

&lt;p&gt;    Testing is essential for building and releasing a bug-free system. From Day 0, create and implement a testing strategy for APIs built using AppSync.&lt;/p&gt;




&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Ensures data protection, confidentiality, and integrity through identity management, security monitoring, and incident response.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Set up Authorisation
&lt;/h4&gt;

&lt;p&gt;    Unprotected APIs are easy targets for attackers seeking valuable data. AppSync offers built-in authorization support with API keys, IAM, OIDC, Cognito user pools, and Lambda authorizers.&lt;/p&gt;

&lt;p&gt;    Use Lambda authoriser for any custom/proprietary authorisation logic.&lt;/p&gt;

&lt;p&gt;    AppSync also has Cognito group-based authorisation that can be easily defined in GraphQL schema.&lt;/p&gt;

&lt;p&gt;    When using API keys, consider rotating them for better security and set up monitoring API key usage to identify any unusual activity, as they are less secure when compared to other types.&lt;/p&gt;

&lt;h4&gt;
  
  
  Use Private API
&lt;/h4&gt;

&lt;p&gt;    For applications running in AWS VPC or on-premises that need access to AppSync APIs, Create private APIs to reduce the attack surface.&lt;/p&gt;

&lt;h4&gt;
  
  
  Set up Web Application Firewall (WAF)
&lt;/h4&gt;

&lt;p&gt;    &lt;a href="https://aws.amazon.com/waf/" rel="noopener noreferrer"&gt;AWS WAF&lt;/a&gt; (Web Application Firewall) is a managed service that helps protect web applications from common threats and vulnerabilities&lt;/p&gt;

&lt;p&gt;    Configuring AWS Web Application Firewall (WAF) for AWS AppSync helps to enhance the security of your GraphQL APIs by providing multiple layers of protection.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create roles with the least permissions
&lt;/h4&gt;

&lt;p&gt;    Roles created for resolvers should be set up with the most limited permissions to adhere to the principle of least privilege.&lt;/p&gt;

&lt;h4&gt;
  
  
  Keep secrets away from resolver code
&lt;/h4&gt;

&lt;p&gt;    You should not hard code API keys or secrets in resolver code; Instead, store it in Secrets Manager or Parameter Store and retrieve it. &lt;/p&gt;

&lt;p&gt;    With a pipeline resolver, the operation to fetch secrets can be executed before processing the external request. One downside would be it will impact the performance as the resolver needs to fetch a secret for each API request.&lt;/p&gt;

&lt;h4&gt;
  
  
  Disable Introspection
&lt;/h4&gt;

&lt;p&gt;    AppSync supports GraphQL introspection, a feature that allows clients to query the schema of a GraphQL API. It enables users to discover the types, fields, queries, mutations, and subscriptions available in the API, as well as the relationships between them.&lt;/p&gt;

&lt;p&gt;    Disable introspection for public users when applicable to enhance security and protect against vulnerabilities and data breaches.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configure query depth limits
&lt;/h4&gt;

&lt;p&gt;    In AWS AppSync, query depth limit refers to a safeguard mechanism that restricts the maximum depth of nested queries allowed in a single GraphQL request. This limit helps prevent excessively deep or complex queries that could lead to performance issues or denial-of-service (DoS) attacks on your API.&lt;/p&gt;

&lt;p&gt;    Enforce reasonable limits on how deep queries can go, ensuring better overall stability and security of the application.&lt;/p&gt;




&lt;h3&gt;
  
  
  Reliability
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Involves designing systems that recover from failures and dynamically meet demand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Leverage Caching
&lt;/h4&gt;

&lt;p&gt;    AppSync supports both full request and per-resolver caching. Enable caching to reduce the load on backend systems.&lt;/p&gt;

&lt;h4&gt;
  
  
  Build reliable Lambdas
&lt;/h4&gt;

&lt;p&gt;    When using a Lambda resolver, implement retry logic to handle transient errors, allowing recovery and improving the function's reliability.&lt;/p&gt;

&lt;p&gt;    Allocate appropriate memory and set reasonable timeout limits based on the lambda's needs to prevent unnecessary failures.&lt;/p&gt;

&lt;p&gt;    As we all know, &lt;em&gt;Everything fails all the time&lt;/em&gt;, implement error handling in Lambdas&lt;/p&gt;

&lt;h4&gt;
  
  
  Enable Pagination
&lt;/h4&gt;

&lt;p&gt;    AppSync uses token-based pagination; a token is used to specify the record after which additional items should be fetched, along with the page size.&lt;/p&gt;

&lt;p&gt;    Implement pagination to reduce the load on backend systems, improving API stability and reliability.&lt;/p&gt;




&lt;h3&gt;
  
  
  Performance Efficiency
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Emphasises using computing resources efficiently to meet system requirements and maintain responsiveness.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Leverage Caching
&lt;/h4&gt;

&lt;p&gt;    Caching boosts the performance and reduces the latency of APIs built with AppSync.&lt;/p&gt;

&lt;h4&gt;
  
  
  Avoid Lambda resolver
&lt;/h4&gt;

&lt;p&gt;    AWS AppSync connects directly with data sources outside of Lambda. Using these connections directly reduces latency by eliminating one network hop, which enhances performance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Optimise Lambda
&lt;/h4&gt;

&lt;p&gt;    When using Lambda resolvers, seek optimisations to enhance performance if Lambda is affecting overall latency.&lt;/p&gt;

&lt;p&gt;    Some optimisation techniques include reducing cold starts, keeping Lambda functions warm, enabling keep-alive for HTTP calls, configuring the required memory, using alternate runtimes like &lt;a href="https://github.com/awslabs/llrt" rel="noopener noreferrer"&gt;LLRT&lt;/a&gt; &amp;amp; &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; and using connection pooling.&lt;/p&gt;

&lt;p&gt;    Consider trade-offs when optimising Lambdas, as premature optimisations may not be necessary and could offer a low Return on Investment (ROI).&lt;/p&gt;

&lt;h4&gt;
  
  
  Utilise Subscriptions Effectively
&lt;/h4&gt;

&lt;p&gt;    AWS AppSync subscriptions are a feature that allows clients to receive real-time updates from a GraphQL API.&lt;/p&gt;

&lt;p&gt;    With AppSync subscriptions, ensure that the payload size is minimal and implement filtering on subscriptions to send only relevant data to clients.&lt;/p&gt;




&lt;h3&gt;
  
  
  Cost Optimisation
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Helps control costs by using the right resources, eliminating waste, and scaling effectively.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Leverage Caching
&lt;/h4&gt;

&lt;p&gt;    Caching reduces the need to invoke resolvers for repeated requests, which in turn lowers the cost of Lambda invocations, DynamoDB queries, and other operations.&lt;/p&gt;

&lt;p&gt;    Setting up caching incurs initial costs since it relies on instances rather than being serverless. However, as AppSync serves more requests using the cache, it reduces overall costs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Avoid Lambda resolver
&lt;/h4&gt;

&lt;p&gt;    As stated in the performance pillar above, Directly interacting with data sources reduces the cost of using Lambda.&lt;/p&gt;

&lt;h4&gt;
  
  
  Log what you need
&lt;/h4&gt;

&lt;p&gt;    Enabling verbose and full logging in AppSync can quickly increase your CloudWatch costs. Choose what to log carefully for troubleshooting.&lt;/p&gt;

&lt;p&gt;    Set a retention period for CloudWatch log groups, as application logs are usually needed for 2-4 weeks for troubleshooting. For longer storage, use AWS S3 with lifecycle policies to reduce costs.&lt;/p&gt;

&lt;p&gt;    AppSync does not support log sampling by default. Implement a custom sampling strategy to log detailed data every X minutes over Y hour(s).&lt;/p&gt;




&lt;h3&gt;
  
  
  Sustainability
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Minimises environmental impact through efficient resource management and sustainable practices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;    There are no sustainability practices to implement as the serverless paradigm continuously increases efficiency and reduces energy consumption as stated in the Serverless Architecture Lens&lt;/p&gt;

&lt;h2&gt;
  
  
  Outro
&lt;/h2&gt;

&lt;p&gt;    Building well-architected AWS AppSync GraphQL APIs requires a comprehensive approach that considers all aspects of the Serverless Architecture Lens. By following best practices and utilizing AWS AppSync's integration capabilities, you can create scalable, secure, and efficient GraphQL APIs for modern applications.&lt;/p&gt;

&lt;p&gt;    Remember, building a well-architected solution is an ongoing process. Regularly review your architecture against these pillars and make improvements as your application evolves.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>aws</category>
      <category>appsync</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Simplify AWS Appsync GraphQL API creation with strongly typed Typescript resolvers</title>
      <dc:creator>sudokar</dc:creator>
      <pubDate>Sun, 30 Jul 2023 07:20:46 +0000</pubDate>
      <link>https://dev.to/sudokar/simplify-aws-appsync-graphql-api-creation-with-strongly-typed-typescript-resolvers-g7b</link>
      <guid>https://dev.to/sudokar/simplify-aws-appsync-graphql-api-creation-with-strongly-typed-typescript-resolvers-g7b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post assumes familiarity with &lt;a href="https://aws.amazon.com/appsync/"&gt;AWS Appsync&lt;/a&gt; and &lt;a href="https://aws.amazon.com/cdk/"&gt;AWS CDK&lt;/a&gt; in general&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this post, we'll look into creating an AWS Appsync GraphQL API with TypeScript using &lt;a href="https://constructs.dev/packages/cdk-appsync-typescript-resolver"&gt;cdk-appsync-typescript-resolver&lt;/a&gt; AWS CDK construct. This powerful construct simplifies the creation of AWS AppSync resolvers in your CDK project, making it easier and more efficient for developers to work with AppSync.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Complete code is available in &lt;a href="https://github.com/sudokar/cdk-appsync-typescript-resolver-demo"&gt;Github&lt;/a&gt; for reference&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Codegen
&lt;/h2&gt;

&lt;p&gt;We can generate Typescript types using &lt;a href="https://the-guild.dev/graphql/codegen"&gt;GraphQL Code Gen&lt;/a&gt; easily.&lt;/p&gt;

&lt;p&gt;These &lt;a href="https://github.com/sudokar/cdk-appsync-typescript-resolver-demo/blob/main/src/types/appsync.ts"&gt;types&lt;/a&gt; are generated using Codegen CLI command &lt;code&gt;graphql-codegen&lt;/code&gt; for this &lt;a href="https://github.com/sudokar/cdk-appsync-typescript-resolver-demo/blob/main/schema.graphql"&gt;graphql schema&lt;/a&gt; using this &lt;a href="https://github.com/sudokar/cdk-appsync-typescript-resolver-demo/blob/main/codegen.ts"&gt;codegen&lt;/a&gt; config file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Alternatively, We can use &lt;a href="https://github.com/aws-amplify/amplify-codegen"&gt;Amplify codegen&lt;/a&gt; as well&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolver in Typescript
&lt;/h2&gt;

&lt;p&gt;We can start writing our resolvers in Typescript using the Codegen generated types and the Appsync's &lt;a href="https://www.npmjs.com/package/@aws-appsync/utils"&gt;utils package&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lets look at an example resolver. &lt;a href="https://github.com/sudokar/cdk-appsync-typescript-resolver-demo/blob/main/src/resolvers/addTodo.ts"&gt;Link to File&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBPutItemRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-appsync/utils&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MutationAddTodoArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../types/appsync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MutationAddTodoArgs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBPutItemRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;attrValues&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PutItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toDynamoDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;attributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toMapValues&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;attrValues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MutationAddTodoArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks a lot better than writing resolvers in Javascript or VTL 😓 &lt;/p&gt;

&lt;p&gt;We can use types for context, various data sources supported by Appsync and &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/built-in-util-js.html"&gt;built-in utilities&lt;/a&gt; along with generated types for GraphQL schema, which enhances the reliability and maintainability code through static type checking&lt;/p&gt;

&lt;h2&gt;
  
  
  Transpiling and Bundling
&lt;/h2&gt;

&lt;p&gt;Unfortunately, we can't just sent the resolvers written in Typescript to Appsync yet. Like we transpile Typescirpt to Javascript for AWS Lambda, We will have to do the same for Appsync.&lt;/p&gt;

&lt;p&gt;Unfortunately, as of today there is no built-in capability in CDK to do this for us. We will have to do it ourself using &lt;a href="https://esbuild.github.io/"&gt;Esbuild&lt;/a&gt; or something similar, which transpiles and bundles code from Typescript to Javascript.&lt;/p&gt;

&lt;p&gt;Using Esbuild's &lt;a href="https://esbuild.github.io/api/#js-sync"&gt;Javascript API&lt;/a&gt;, We have a custom CDK construct &lt;a href="https://constructs.dev/packages/cdk-appsync-typescript-resolver"&gt;cdk-appsync-typescript-resolver&lt;/a&gt; that will automate the transpilation and bundling process for us.&lt;/p&gt;

&lt;p&gt;An example of creating Typescript function using the custom construct &lt;a href="https://constructs.dev/packages/cdk-appsync-typescript-resolver/v/0.0.4/api/AppsyncTypescriptFunction?lang=typescript"&gt;AppsyncTypescriptFunction&lt;/a&gt;. This abstracts the transpiling and bundling process and setting default properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AppsyncTypescriptFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AddTodoFunction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addTodo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;graphqlApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;dataSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dynamoDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addTodo.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;replaceStrings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JS resolvers doesn't support unit resolvers, supports only &lt;a href="https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html#anatomy-of-a-pipeline-resolver-js"&gt;pipeline resolvers&lt;/a&gt;. When we want to create a unit resolver, we will have to use bit of boilerplate code and settings to make the pipeline resolver as unit resolver.&lt;/p&gt;

&lt;p&gt;An added functionality to the construct is to define a property &lt;code&gt;replaceStrings&lt;/code&gt;, which takes a map of from and to strings, used to replace strings in the transpiled and bundled code.&lt;/p&gt;

&lt;p&gt;We have another CDK construct &lt;a href="https://constructs.dev/packages/cdk-appsync-typescript-resolver/v/0.0.4/api/TSExpressPipelineResolver?lang=typescript"&gt;TSExpressPipelineResolver&lt;/a&gt; to use the &lt;a href="https://constructs.dev/packages/cdk-appsync-typescript-resolver/v/0.0.4/api/AppsyncTypescriptFunction"&gt;AppsyncTypescriptFunction&lt;/a&gt; and sets up required boilerplate for us.&lt;/p&gt;

&lt;p&gt;An example of using &lt;code&gt;TSExpressPipelineResolver&lt;/code&gt; construct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;TSExpressPipelineResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AddTodoResolver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;graphqlApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;typeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mutation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fieldName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addTodo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tsFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We can use instance of &lt;code&gt;AppsyncTypescriptFunction&lt;/code&gt; construct, which extends &lt;code&gt;appsync.AppsyncFunction&lt;/code&gt;, in a regular &lt;code&gt;appsync.Resolver&lt;/code&gt; construct too&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;You can find the complete code of this demo on &lt;a href="https://github.com/sudokar/cdk-appsync-typescript-resolver-demo"&gt;GitHub&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="nx"&gt;deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tip
&lt;/h2&gt;

&lt;p&gt;Catch invalid syntax (things not supported by Appsync's JS Resolver) while writing resolvers using the Eslint plugin &lt;a href="https://www.npmjs.com/package/@aws-appsync/eslint-plugin"&gt;@aws-appsync/eslint-plugin&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;By using the constructs from &lt;a href="https://constructs.dev/packages/cdk-appsync-typescript-resolver"&gt;cdk-appsync-typescript-resolver&lt;/a&gt;, we can now take advantage of static typing and code editor's IntelliSense &amp;amp; inlay hints to make Appsync's resolvers easy and efficient. Generation of types from GraphQL schema makes it even more efficient. And also, With &lt;code&gt;@aws-appsync/eslint-plugin&lt;/code&gt; we can get the benefit of finding invalid syntax while writing code.&lt;/p&gt;

&lt;p&gt;Hope this was helpful!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>appsync</category>
      <category>typescript</category>
      <category>cdk</category>
    </item>
  </channel>
</rss>
