|
| 1 | +export const description = |
| 2 | + 'Introducing the concept of Infrastructure from Code (IfC) and how Nitric uses it to automate infrastructure and deployment.' |
| 3 | + |
| 4 | +# Infrastructure from Code (IfC) |
| 5 | + |
| 6 | +Infrastructure from Code (IfC) is an extension of Infrastructure as Code (IaC) that takes runtime application code into consideration and enhances Separation of Concerns (SoC). |
| 7 | + |
| 8 | +<Note> |
| 9 | + Several products describe themselves as "Infrastructure from Code" (IfC), but |
| 10 | + they're not all like Nitric. Many are platforms/SaaS products, not a |
| 11 | + framework. Those products have different benefits and trade-offs. |
| 12 | +</Note> |
| 13 | + |
| 14 | +Nitric's Infrastructure from Code (IfC) automates the creation of infrastructure from your application code, ensuring a clear separation between application logic and deployment concerns. |
| 15 | + |
| 16 | +It abstracts the infrastructure layer, allowing developers to define what their application needs without getting bogged down in the specifics of cloud services or infrastructure configurations. |
| 17 | + |
| 18 | +Sometimes it's easier to explain IfC by exploring the benefits. |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Rapid Development |
| 23 | + |
| 24 | +Nitric significantly reduces or removes cloud-specific code. For example, accessing a file in a cloud storage bucket with Nitric is just a few lines of code, while the equivalent code using cloud provider SDKs is much more verbose and error prone: |
| 25 | + |
| 26 | +<CodeGroup> |
| 27 | + |
| 28 | +```javascript {{ title:"Nitric SDK" }} |
| 29 | +import { bucket } from '@nitric/sdk' |
| 30 | + |
| 31 | +const profiles = bucket('profiles').allow('read') |
| 32 | + |
| 33 | +export const getProfilePic = async (filename) => { |
| 34 | + return await profiles.file(filename).read() |
| 35 | +}) |
| 36 | +``` |
| 37 | + |
| 38 | +```javascript {{ title:"AWS SDK" }} |
| 39 | +import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3' |
| 40 | + |
| 41 | +const client = new S3Client({}) |
| 42 | +const PROFILES_BUCKET = process.env.PROFILES_BUCKET |
| 43 | + |
| 44 | +if (!bucket) { |
| 45 | + throw new Error('PROFILES_BUCKET environment variable not set') |
| 46 | +} |
| 47 | + |
| 48 | +export const getProfilePic = async (filename) => { |
| 49 | + const command = new GetObjectCommand({ |
| 50 | + Bucket: PROFILES_BUCKET, |
| 51 | + Key: filename, |
| 52 | + }) |
| 53 | + |
| 54 | + const response = await client.send(command) |
| 55 | + return await response.Body.transformToByteArray() |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +</CodeGroup> |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +## Decoupled Applications and Infrastructure |
| 64 | + |
| 65 | +Nitric keeps application code independent of specific cloud services. Developers focus on designing the application architecture and building features, without first making long-term technology choices. |
| 66 | + |
| 67 | +For example this same code would work equally well backed by AWS SNS, AWS EventBridge, Google Cloud Pub/Sub, Azure EventGrid, Apache Kafka, or any other messaging service: |
| 68 | + |
| 69 | +```javascript |
| 70 | +import { topic } from '@nitric/sdk' |
| 71 | + |
| 72 | +const myTopic = topic('my-topic').allow('publish') |
| 73 | + |
| 74 | +export const publishMessage = async (message) => { |
| 75 | + await myTopic.publish(message) |
| 76 | +} |
| 77 | +``` |
| 78 | + |
| 79 | +We can change the underlying messaging service through a simple configuration change, without modifying the application code. |
| 80 | + |
| 81 | +<CodeGroup> |
| 82 | + |
| 83 | +```yaml {{ label:"nitric.prod.yaml", title:"AWS" }} |
| 84 | +provider: nitric/[email protected] |
| 85 | +``` |
| 86 | +
|
| 87 | +```yaml {{ label:"nitric.prod.yaml", title:"Azure" }} |
| 88 | +provider: nitric/[email protected] |
| 89 | +``` |
| 90 | +
|
| 91 | +```yaml {{ label:"nitric.prod.yaml", title:"Google Cloud" }} |
| 92 | +provider: nitric/[email protected] |
| 93 | +``` |
| 94 | +
|
| 95 | +```yaml {{ label:"nitric.prod.yaml", title:"Anything Else" }} |
| 96 | +provider: your_namespace/[email protected] |
| 97 | +``` |
| 98 | +
|
| 99 | +</CodeGroup> |
| 100 | +
|
| 101 | +<Note> |
| 102 | + The runtime code for cloud services like SNS still exists, it's just isolated |
| 103 | + to an independent module. It could be from a [Nitric |
| 104 | + provider](https://github.com/nitrictech/nitric/blob/main/cloud/aws/runtime/topic/sns.go) |
| 105 | + or [something custom](/reference/providers/custom/building-custom-provider) |
| 106 | + built by you or an independent team, such as platform engineers or DevOps |
| 107 | + teams. |
| 108 | +</Note> |
| 109 | +
|
| 110 | +--- |
| 111 | +
|
| 112 | +## Automate Application Infrastructure Requirements |
| 113 | +
|
| 114 | +Using Infrastructure as Code (IaC) tools like Terraform, Pulumi, or AWS CDK, developers can define the infrastructure requirements for their application. You can even create reusable modules for common infrastructure patterns. For example, you could create a pattern for async messaging, which includes a topic, and a subscription bound to a compute function. |
| 115 | +
|
| 116 | +The problem is that these tools in isolation require manual processes to keep the infrastructure in sync with the application code. Processes that are error-prone and time-consuming. |
| 117 | +
|
| 118 | +Infrastructure lingers in production that's no longer in use, permissions are too broad creating security risks, or too strict causing application failures. Environment variables used to determine resource names can be can be broken by a simple typo. It's a mess. |
| 119 | +
|
| 120 | +Nitric solves this by automatically generating a requirements specification from the application code. This specification describes the resources used by the application, their hierarchy, and how the application intends to use them at runtime. |
| 121 | +
|
| 122 | +If you want to see the spec for your application you can run `nitric spec`: |
| 123 | + |
| 124 | +```bash |
| 125 | +nitric spec |
| 126 | +``` |
| 127 | + |
| 128 | +### No Rogue Resources or Permissions |
| 129 | + |
| 130 | +Resources and access requirements are defined as close as possible to where they're used - so it's easy to see when they're no longer needed or permissions are too broad. |
| 131 | + |
| 132 | +```javascript |
| 133 | +import { queue } from '@nitric/sdk' |
| 134 | +
|
| 135 | +// Delete this line and the queue disappears from the IaC |
| 136 | +const myQueue = queue('my-queue').allow('enqueue') |
| 137 | +
|
| 138 | +// It's easy to tell when `enqueue` is no longer needed |
| 139 | +export const enqueueMessage = async (message) => { |
| 140 | + await myQueue.enqueue(message) |
| 141 | +} |
| 142 | +``` |
| 143 | + |
| 144 | +### No Broken Environment Variables |
| 145 | + |
| 146 | +The name of a resource is defined **_once_** (in the code), instead of twice (in the code and the IaC). IaC configuration is generated, so it's never out of sync with the application code. |
| 147 | + |
| 148 | +```javascript |
| 149 | +sql('profiles') |
| 150 | +``` |
| 151 | + |
| 152 | +and the framework maps the requirements specification to plugins written with existing IaC tools. These tools are still responsible for provisioning the resources, roles, and permissions. |
| 153 | + |
| 154 | +### Don't Change App Code for Infrastructure Changes |
| 155 | + |
| 156 | +You haven't imported the AWS SDK, Google Cloud SDK, or Azure SDK. You haven't written any cloud-specific code. You haven't written any mocks or tests for these cloud services, or anything that makes your code less portable. |
| 157 | + |
| 158 | +So when changes are needed for performance, cost, or compliance, you can make them instantly. It's just a line of config. |
0 commit comments