diff --git a/src/components/Libraries.tsx b/src/components/Libraries.tsx index ca82b0a10..ac0d0342c 100644 --- a/src/components/Libraries.tsx +++ b/src/components/Libraries.tsx @@ -49,7 +49,33 @@ const libraries = [ }, ] -export function Libraries() { +export interface LibrariesProps { + minimal?: boolean +} + +export function Libraries({ minimal = false }: LibrariesProps) { + if (minimal) { + return ( +
+ {libraries.map((library) => ( + + {library.name + + ))} +
+ ) + } + return (
diff --git a/src/pages/concepts/infrastructure-from-code.mdx b/src/pages/concepts/infrastructure-from-code.mdx new file mode 100644 index 000000000..d88f04450 --- /dev/null +++ b/src/pages/concepts/infrastructure-from-code.mdx @@ -0,0 +1,158 @@ +export const description = + 'Introducing the concept of Infrastructure from Code (IfC) and how Nitric uses it to automate infrastructure and deployment.' + +# Infrastructure from Code (IfC) + +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). + + + Several products describe themselves as "Infrastructure from Code" (IfC), but + they're not all like Nitric. Many are platforms/SaaS products, not a + framework. Those products have different benefits and trade-offs. + + +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. + +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. + +Sometimes it's easier to explain IfC by exploring the benefits. + +--- + +## Rapid Development + +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: + + + +```javascript {{ title:"Nitric SDK" }} +import { bucket } from '@nitric/sdk' + +const profiles = bucket('profiles').allow('read') + +export const getProfilePic = async (filename) => { + return await profiles.file(filename).read() +}) +``` + +```javascript {{ title:"AWS SDK" }} +import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3' + +const client = new S3Client({}) +const PROFILES_BUCKET = process.env.PROFILES_BUCKET + +if (!bucket) { + throw new Error('PROFILES_BUCKET environment variable not set') +} + +export const getProfilePic = async (filename) => { + const command = new GetObjectCommand({ + Bucket: PROFILES_BUCKET, + Key: filename, + }) + + const response = await client.send(command) + return await response.Body.transformToByteArray() +} +``` + + + +--- + +## Decoupled Applications and Infrastructure + +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. + +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: + +```javascript +import { topic } from '@nitric/sdk' + +const myTopic = topic('my-topic').allow('publish') + +export const publishMessage = async (message) => { + await myTopic.publish(message) +} +``` + +We can change the underlying messaging service through a simple configuration change, without modifying the application code. + + + +```yaml {{ label:"nitric.prod.yaml", title:"AWS" }} +provider: nitric/aws@1.1.1 +``` + +```yaml {{ label:"nitric.prod.yaml", title:"Azure" }} +provider: nitric/azure@1.1.1 +``` + +```yaml {{ label:"nitric.prod.yaml", title:"Google Cloud" }} +provider: nitric/gcp@1.1.1 +``` + +```yaml {{ label:"nitric.prod.yaml", title:"Anything Else" }} +provider: your_namespace/custom@1.0.0 +``` + + + + + The runtime code for cloud services like SNS still exists, it's just isolated + to an independent module. It could be from a [Nitric + provider](https://github.com/nitrictech/nitric/blob/main/cloud/aws/runtime/topic/sns.go) + or [something custom](/reference/providers/custom/building-custom-provider) + built by you or an independent team, such as platform engineers or DevOps + teams. + + +--- + +## Automate Application Infrastructure Requirements + +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. + +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. + +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. + +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. + +If you want to see the spec for your application you can run `nitric spec`: + +```bash +nitric spec +``` + +### No Rogue Resources or Permissions + +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. + +```javascript +import { queue } from '@nitric/sdk' + +// Delete this line and the queue disappears from the IaC +const myQueue = queue('my-queue').allow('enqueue') + +// It's easy to tell when `enqueue` is no longer needed +export const enqueueMessage = async (message) => { + await myQueue.enqueue(message) +} +``` + +### No Broken Environment Variables + +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. + +```javascript +sql('profiles') +``` + +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. + +### Don't Change App Code for Infrastructure Changes + +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. + +So when changes are needed for performance, cost, or compliance, you can make them instantly. It's just a line of config. diff --git a/src/pages/index.mdx b/src/pages/index.mdx index 9a800078b..6a78076a1 100644 --- a/src/pages/index.mdx +++ b/src/pages/index.mdx @@ -1,9 +1,6 @@ -import { Community } from '@/components/Community' import { HomeHeader } from '@/components/HomeHeader' +import { InstallNitric } from '@/components/InstallNitric' import { Libraries } from '@/components/Libraries' -import { Providers } from '@/components/Providers' -import { GettingStarted } from '@/components/GettingStarted' -import { Concepts } from '@/components/Concepts' export const description = 'Documentation for the Nitric cloud application framework.' @@ -12,17 +9,387 @@ export const disableEditGithub = true - +--- - +Nitric is a declarative cloud framework with common resources like APIs, websockets, databases, queues, topics, buckets, and more. -{/* */} +However, Nitric doesn't just deploy the resources, it helps you interact with them. It also makes them pluggable, so you can swap services or even whole clouds without changing your code. - +It's what's missing between applications and infrastructure automation. - +Oh, and it supports basically any language, like JavaScript, TypeScript, Python, Go, you name it. - +
+ +
+ +If you're familiar Nitric already, you might want to jump to the [Installation](/getting-started/installation), [Guides](/guides) or [Resources](/apis) sections. Otherwise, keep reading to learn more about Nitric. + +--- + +## Services + +Services are the heart of Nitric apps, they're the entrypoints to your code. They can serve as APIs, websockets, schedule handlers, subscribers and a lot more. You create services by telling Nitric where to look for your code and how to run it. + +```yaml {{ label:"nitric.yaml" }} +name: example +services: + - match: services/*.ts + start: npm run dev:services $SERVICE_PATH +``` + +You might have one service that handles everything, or a service for each route. It's up to you. Every matched service becomes a container, allowing them run and scale independently. + +--- + +## Resources + +Resources are the building blocks of your apps. They're the databases, queues, buckets, etc. that your services interact with. To request a resource, import the resource type and create one with a name. + + + +```javascript {{ label:"services/example.js" }} +import { bucket } from '@nitric/sdk' + +const profiles = bucket('profiles').allow('read', 'write', 'delete') +``` + +```python {{ label:"services/example.py" }} +from nitric.resources import bucket +from nitric.application import Nitric + +profiles = bucket('profiles').allow('read', 'write', 'delete') + +Nitric.run() +``` + +```go {{ label:"services/example/main.go" }} +import ( + "fmt" + + "github.com/nitrictech/go-sdk/nitric" +) + +func main() { + profiles, _ := nitric.NewBucket("profiles").Allow(nitric.BucketRead, nitric.BucketWrite, nitric.BucketDelete) + + if err := nitric.Run(); err != nil { + fmt.Println(err) + } +} +``` + +```dart {{ label:"services/example.dart" }} +import 'package:nitric_sdk/nitric.dart'; + +void main() { + final profiles = Nitric.bucket("profiles").allow([ + BucketPermission.read, + BucketPermission.write, + BucketPermission.delete, + ]); +} +``` + + + +Nitric collects everything your services request. When you deploy, the deployment plugin you choose creates the resources and services, then links them all together. + + + That might sound like magic, but it's more like a compiler for + Infrastructure-as-Code - you can read about it in the + [Concepts](/concepts/introduction) section. + + +--- + +## Deployment Plugins (Providers) + +Nitric is designed to be independent of any platform, so you can run your apps anywhere. You can deploy to AWS, Azure, GCP, your own Kubernetes cluster or a single server. You could even deploy to multiple clouds at once. + +Are the differences between a bucket on AWS and a bucket on Azure important to most apps? We don't think so. So why should the code be different? + +Nitric abstracts away API layer differences, so you can focus on your app. The part that makes that possible is a plugin, we call a **Provider**. + + + +```yaml {{ label:"nitric.prod.yaml", title: "AWS Pulumi" }} +provider: nitric/aws@1.1.1 +region: us-east-1 +``` + +```yaml {{ label:"nitric.prod.yaml", title: "Azure Pulumi" }} +provider: nitric/azure@1.1.1 +region: East US +``` + +```yaml {{ label:"nitric.prod.yaml", title: "Google Pulumi" }} +provider: nitric/gcp@1.1.1 +region: us-east1 +``` + +```yaml {{ label:"nitric.prod.yaml", title: "AWS Terraform" }} +provider: nitric/awstf@1.1.1 +region: us-east-1 +``` + +```yaml {{ label:"nitric.prod.yaml", title: "Google Terraform" }} +provider: nitric/gcptf@1.1.1 +region: us-east1 +``` + +```yaml {{ label:"nitric.prod.yaml", title: "Custom" }} +# Build your own provider to deploy anywhere you like. +provider: custom/on-prem@1.0.0 +``` + + + +We have several providers built-in with IaC from [Pulumi](https://www.pulumi.com/) or [Terraform](https://www.terraform.io/), but you can build your own with any tools you prefer and **deploy anywhere**. + +--- + +## Projects + +Projects built with Nitric don't have many restrictions. You can use most languages, libraries, tools, clouds, services, mostly anything you like. But, you need to have a `nitric.yaml` file in the root of your project. + +```yaml {{ label:"nitric.yaml" }} +name: example +services: + - match: services/*.ts + start: npm run dev:services $SERVICE_PATH +``` + +Nitric uses this to find your services, then it turns each service into a container, runs them in deployment mode to match the resources you requested and gives the result to the Provider - which either generates IaC (like Terraform) or automatically deploys your app. + +So, a project structure might look something like this: + + + +```plaintext {{ title:"JavaScript" }} +example/ +├── nitric.yaml +├── services/ +│ └── orders.js +│ └── users.js +└── package.json +``` + +```plaintext {{ title:"Python" }} +example/ +├── nitric.yaml +├── services/ +│ └── orders.py +│ └── users.py +└── Pipfile +``` + +```plaintext {{ title:"Go" }} +example/ +├── nitric.yaml +├── services/ +│ ├── orders/ +│ │ └── main.go +│ └── users/ +│ └── main.go +├── dockerfile +└── go.mod +``` + +```plaintext {{ title:"Dart" }} +example/ +├── nitric.yaml +├── services/ +│ └── orders.dart +│ └── users.dart +└── pubspec.yaml +``` + + + +--- + +## CLI + +Nitric has a CLI to help you create, manage, run and deploy your projects. We recommend installing it with a package manager: + + + +```bash {{ title: 'macOS' }} +brew install nitrictech/tap/nitric +``` + +```bash {{ title: 'Windows' }} +scoop bucket add nitric https://github.com/nitrictech/scoop-bucket.git +scoop install nitric +``` + +```bash {{ title: 'Linux' }} +curl -L "https://nitric.io/install?version=latest" | bash +``` + + + + + Nitric has a few dependencies, like Docker, which you can read about in the + [Installation](/getting-started/installation) section. + + +### New + +You can create a new project from a template with the `new` command. + +```bash +nitric new +``` + +### Start + +You can run your project locally with the `start` command. + +```bash +nitric start +``` + + + Start also emulates the resources you requested, so you can test your app + locally. And provides a Dashboard UI to interact with the resources. + + +### Deploy + +You can deploy your project with the `up` command, once you have a stack file (deployment target). + +```bash +# Create a new stack (deployment target) +nitric stack new +# Deploy to the stack, using the provider in the stack file +nitric up +``` + + + Some providers deploy directly, others generate IaC files for existing tools + (like HCL for Terraform). In the cases where IaC is generated you use the + chosen IaC tool to deploy (e.g. `terraform apply`). + +Currently, all Pulumi providers deploy directly, while Terraform providers generate IaC. + + + +### Tear Down + +You can tear down your project with the `down` command. + +```bash +nitric down +``` + + + If you used a provider that generates IaC, use the IaC tool to tear down (e.g. + `terraform destroy`). + + +--- + +## Local development + +Sometimes you'll want to run your app locally. We don't mean "binds to a cloud environment and syncs your code, but doesn't work without Wifi" local, we mean "runs on your machine, on a desert island" local. + +`nitric start` hosts entrypoints like APIs, websockets, and schedules, as well as resources like databases, topics, queues, key/value stores and buckets. + +It also provides a Dashboard to interact with your resources, so you can trigger schedules without waiting, or topics without a publisher, or upload test files to a bucket. It even produces a live architecture diagram of your services and resources as your code updates. + +screen shot of the local development dashboard + +--- + +## Extension & Escape Hatches + +The services of cloud providers are vast, we can't cover everything and we shouldn't. Many services are unique enough that abstraction would be a disservice. Nitric aims to make common "table stakes" services easy to use, but we never want to limit you. + +Here are some ways you can extend or escape Nitric. + +### Runtime + +If you need to access a service in a way Nitric doesn't support, you can always use the provider's SDK directly. Code like this will always work: + + + +```javascript +// Import the AWS SDK +import { S3Client } from '@aws-sdk/client-s3' + +// Use it like you normally would +const s3 = new S3Client({ region: 'us-east-1' }) +const { Contents } = await s3.listObjectsV2({ Bucket: 'my-bucket' }) +``` + +```python +# Import the boto3 (AWS) SDK +from boto3 import client + +# Use it like you normally would +s3 = client('s3', region_name='us-east-1') +response = s3.list_objects_v2(Bucket='my-bucket') +``` + +```go +import ( + "context" + // Import the AWS SDK + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" +) + +// Use it like you normally would +cfg, _ := config.LoadDefaultConfig(context.TODO()) +client := s3.NewFromConfig(cfg) + +output, _ := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{ + Bucket: aws.String("my-bucket"), +}) +``` + +```dart +// Import the AWS SDK +import 'package:aws_s3/aws_s3.dart'; + +// Use it like you normally would +final s3 = S3(region: 'us-east-1'); +final response = await s3.listObjectsV2(Bucket: 'my-bucket'); +print(response.contents); +``` + + + +### Overriding + +If you need to change how Nitric deploys a resources or how it interacts with a service at runtime, you can [extend or modify a provider](/reference/providers/custom/extend-standard-provider). + + + For example, here's a [project that swaps SNS for + EventBridge](https://github.com/jyecusch/iac-ifc-comparison) on AWS. + + +### Full Customization + +If you need to deploy to a new platform or new set of services that Nitric doesn't support, you can [build your own provider](/reference/providers/custom/building-custom-provider). This is a bit more advanced, but it's the ultimate escape hatch. + +The included providers are written in Go and built using Terraform or Pulumi, but you can use any language or tool you like. + +### Additional Resources + +If you need to use a service/resource that Nitric doesn't support, you do that like you always would. Nitric doesn't get in the way of you using the cloud, it just makes it easier. + +--- + +## What now? + +If you're still not sure what to make of Nitric, maybe start with the [Concepts](/concepts/introduction) section, otherwise the best way to learn is to dive into the [Guides](/guides) and start building your first project. diff --git a/typography.js b/typography.js index 05f150473..1448fd268 100644 --- a/typography.js +++ b/typography.js @@ -27,7 +27,7 @@ module.exports = ({ theme }) => ({ '--tw-prose-invert-bold': theme('colors.white'), '--tw-prose-invert-counters': theme('colors.zinc.400'), '--tw-prose-invert-bullets': theme('colors.zinc.600'), - '--tw-prose-invert-hr': theme('colors.white / 0.05'), + '--tw-prose-invert-hr': theme('colors.white / 0.10'), '--tw-prose-invert-quotes': theme('colors.zinc.100'), '--tw-prose-invert-quote-borders': theme('colors.zinc.700'), '--tw-prose-invert-captions': theme('colors.zinc.400'), @@ -143,8 +143,8 @@ module.exports = ({ theme }) => ({ hr: { borderColor: 'var(--tw-prose-hr)', borderTopWidth: 1, - marginTop: theme('spacing.16'), - marginBottom: theme('spacing.16'), + marginTop: theme('spacing.8'), + marginBottom: theme('spacing.8'), maxWidth: 'none', marginLeft: `calc(-1 * ${theme('spacing.4')})`, marginRight: `calc(-1 * ${theme('spacing.4')})`,