Skip to content

Commit

Permalink
finish first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
robertjdominguez committed Mar 16, 2024
1 parent 860628f commit 214a67e
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 38 deletions.
69 changes: 64 additions & 5 deletions docs/getting-started/1-create-a-project.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ keywords:
- supergraph
---

import Thumbnail from "@site/src/components/Thumbnail";

# Create a Project

## Introduction
Expand Down Expand Up @@ -92,7 +94,7 @@ ddn quickstart

### Step 3.1: Choose a host

We'll be prompted to choose the data delivery network to which we want to host our project:
We'll be prompted to choose the data delivery network on which we want to host our project:

```bash
? Choose the DDN to create your project on [Use arrows to move, type to filter]
Expand Down Expand Up @@ -164,8 +166,15 @@ INF Data connector app_connector set up successfully
When we enter our connection string, the CLI will introspect our data source and ask us if we'd like to track all of our
tables and relationships; we'll select `y` as this will automatically write our metadata for us! 🎉

:::danger HEY!

NOT YET READY VIA CLI — IF YOU'RE FOLLOWING ALONG FOR TESTING, TRACK THINGS VIA THE VS CODE EXTENSION ONCE YOU RUN DDN
DEV

:::

```bash
TODO...NOT YET READY VIA CLI — IF YOU'RE FOLLOWING ALONG FOR TESTING, TRACK THINGS VIA THE VS CODE EXTENSION
TODO when above is complete
```

At this point, the CLI will ask us if we want to connect another data source. For now, we don't, so type `n` and hit
Expand All @@ -175,7 +184,7 @@ At this point, the CLI will ask us if we want to connect another data source. Fo

We won't use this until later in the guide, but we can go ahead and add the NodeJS/TypeScript connector to incorporate
our own custom business logic into our API. When prompted, type `y` and hit `ENTER`. Then, let's name this connector
`ts_connector`:
`app_functions`:

```bash
? Do you want add custom business logic using NodeJS/TypeScript functions? y
Expand Down Expand Up @@ -203,8 +212,8 @@ INF Run 'cd my-first-supergraph && ddn dev' to build your GraphQL API.

## Step 4: Build your GraphQL API

If we run the provided command from the previous step, we'll see the CLI output information our first build, including
the URL for our project's Console 🎉
If we run the provided command from the previous step's output, we'll see the CLI created our first build, including the
URL for our project's Console 🎉

```bash
INF Preparing project...
Expand Down Expand Up @@ -247,6 +256,56 @@ INF Doing a supergraph build...
+---------------+--------------------------------------------------------------------+
```
:::info What's a build?
<details>
<summary>Click to learn more »</summary>
This is the first concept we've introduced in this guide. [Builds](/ci-cd/builds.mdx) are a new concept in Hasura that
allow you to quickly iterate and prototype on your project's metadata. A build is an immutable, fully-functioning
GraphQL API that represents a milestone in your development cycle.
It may be helpful to think of builds as git commits. Since each is deployed on Hasura DDN, it can be shared with other
users.
Each build is completely independent. One project can have multiple builds, out of which, one is applied to production.
This workflow allows for easier rollbacks on production, and greater collaboration during development.
</details>
:::
## Step 5: Run your first query
We're using the docs sample app's schema for this guide's visuals, but you can use the GraphiQL Explorer to create your
query or write it manually:
<Thumbnail src="/img/get-started/0.0.1_console-execute-query-on-build.png" alt="Execute a query" width="1000px" />
<details>
<summary>Using our sample db? You can use this query!</summary>
```graphql
query OrdersQuery {
orders {
id
status
delivery_date
user {
id
name
email
}
product {
id
name
}
}
}
```
</details>
## What just happened?
When you ran the `quickstart` command, the CLI created a new project for you on Hasura DDN. It also scaffolded out all
Expand Down
184 changes: 155 additions & 29 deletions docs/getting-started/2-add-business-logic.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ keywords:
- typescript
---

import Thumbnail from "@site/src/components/Thumbnail";

# Add Business Logic

## Introduction
Expand All @@ -24,8 +26,8 @@ can be made available over your GraphQL API as a query or a mutation.
## Step 1: Navigate to the TypeScript connector

When we ran `ddn quickstart`, we told the CLI we wanted to include the TypeScript connector; inside of the
`app/ts_connector` directory, we can find all the files the CLI generated, including `functions.ts` which will house our
custom logic:
`/app/app_functions` directory, we can find all the files the CLI generated, including `functions.ts` which will house
our custom logic:

```bash
├── builds
Expand All @@ -34,9 +36,9 @@ custom logic:
# highlight-end
│ ├── package-lock.json
│ ├── package.json
│ ├── ts_connector.build.hml
│ ├── app_functions.build.hml
│ └── tsconfig.json
└── ts_connector.hml
└── app_functions.hml
```

If we open `functions.ts`, we'll see the following contents:
Expand Down Expand Up @@ -96,32 +98,52 @@ Of course, our API will need more than one function. Let's create a new function
`functions.ts` that yells at a user if they do something they're not supposed to do:

```ts
// functions.ts

/**
* @readonly
*/
export function scoldUser(username: string) {
return `DON'T DO THAT, ${username.toUpperCase()}!`;
export function scoldUser(userId: string) {
return `DON'T DO THAT, ${userId}!`;
}
```

We need to make sure our connector is aware of this function by tracking it. If we look inside our `ts_connector.hml`
We need to make sure our connector is aware of this function by tracking it. If we look inside our `app_functions.hml`
file, we'll see the key-value pair with a warning for our new function. If we hover over the warning line, we can use
code actions (quick fix) to track the new function and add it to our API:

// TODO: Add screenshot
<Thumbnail src="/img/get-started/0.0.1_vs-code_track-new-function.png" alt="Tracking new function" width="1000px" />

:::info Storing tracked functions

After selecting this code action, the extension will ask you where you'd like to save the metadata for `scoldUser()`.
Choose the `Default` option to store it in its own file for ease and organization. This will create a new file in
`commands` called `scoldUser.hml`.

:::

:::danger HEY!

NOT YET READY VIA CLI — IF YOU'RE FOLLOWING ALONG FOR TESTING, KILL DEV MODE AND RESTART IT TO HAVE THE NEW COMMAND BE
AVAILABLE IN YOUR API

This will trigger a rebuild of our connector and our new function will be available in our API:
:::

This will trigger a rebuild of our connector and our new function will instantly be available in our API. We can test it
by running the following query:

```graphql
query MyQuery {
scoldUser(userId: "7cf0a66c-65b7-11ed-b904-fb49f034fbbb")
}
```

// TODO: Add screenshot
<Thumbnail src="/img/get-started/0.0.1_console_run-scoldUser.png" alt="Scold a user" width="1000px" />

## Step 4: Import a package

The TypeScript connector acts like any NodeJS project; this means you can use all the packages you're already familiar
with. Let's add the `pg` package so we can directly interact with our database.

Inside the `app/ts_connector/builds` directory, run the following:
Inside the `/app/app_functions/builds` directory, run the following:

```bash
npm i pg
Expand All @@ -148,7 +170,7 @@ export async function scoldUser(userId: string) {
const client = new Client({
host: "35.236.11.122",
port: 5432,
database: "docs-sample-app",
database: "v3-docs-sample-app",
user: "read_only_user",
password: "readonlyuser",
});
Expand All @@ -167,36 +189,140 @@ export async function scoldUser(userId: string) {
}
```

This modifies our function to accept an `id` for a user. We'll use this to determine who our user is and scold them.
After modifying the `function.ts` file, try this query in your API:
We'll use this to determine who our user is and scold them by name.

After modifying the `functions.ts` file, re-run the same query in your API and you should see the following returned:

```json
{
"data": {
"scoldUser": "DON'T DO THAT, SEAN!"
}
}
```

## Step 5: Create a relationship

It would be nice to call this function as part of a larger query for a user. We can create relationships **directly**
between our commands and models. That means we can call `scoldUser` directly on Sean from our API without requiring
additional queries, and — if we'd like — can even return other related data.

Find the `users.hml` file in `/app_connector/models` and add a new entry at the bottom of the file by adding `---` and
hitting `ENTER`.

Once you hit enter, you should see Hasura's LSP kick in and provide you with the variety of options of new metadata
objects you can add. If you start typing `relationship`, choose the `Relationship (to command)` option, and hit `ENTER`,
the LSP will scaffold out your metadata object.

<Thumbnail src="/img/get-started/0.0.1_vs-code_add-relationship.png" alt="Add a relationship" width="1000px" />

You can tab through each key-value pair; the LSP will assist you by returning available options based on your data
sources. Your final relationship should look like this:

```yaml
---
kind: Relationship
version: v1
definition:
name: scold_user
source: users
target:
command:
name: scoldUser
mapping:
- source:
fieldPath:
- fieldName: id
target:
argument:
argumentName: userId
```
Upon saving the file, your API will be re-built. If you head to your project's console and run the following query, you
can directly call your TypeScript function from your API based on your user's ID:
```graphql
query MyQuery {
scoldUser(userId: "7cf0a66c-65b7-11ed-b904-fb49f034fbbb")
users(where: { id: { _eq: "7cf0a66c-65b7-11ed-b904-fb49f034fbbb" } }) {
scold_user
}
}
```

You should see the following returned:
And the response is:

```json
```JSON
{
"data": {
"scoldUser": "DON'T DO THAT, SEAN!"
"users": [
{
"scold_user": "DON'T DO THAT, SEAN!"
}
]
}
}
```

## Step 5: Create a relationship
## Step 6: Add permissions

The reality is, we don't need the `pg` package. We can create relationships directly between our commands and models.
That means we can call `scoldUser` directly on Sean from our API without requiring any extra packages. Find the... TODO
Of course, we don't want _any_ user accessing this command from the API. With both models and commands, we can
declaratively restrict access to specific roles using permissions.

## Step 6: Add permissions
Find the `scoldUser.hml` file in `/app/app_functions/commands` and let's use the LSP to help us create a permission.
Within this file, you'll see `CommandPermissions` for `scoldUser` command. There's already a permissions array with the
role of `admin` enabled by default.

<Thumbnail
src="/img/get-started/0.0.1_vs-code_existing-user-permissions.png"
alt="Default command permissions"
width="1000px"
/>

Let's add a new item to the array for a `user` role and disallow execution:

```yaml
---
kind: CommandPermissions
version: v1
definition:
commandName: scoldUser
permissions:
- role: admin
allowExecution: true
- role: user
allowExecution: false
```
If you head back to your API, you can add the following key-value pair to test our new permission:
| Key | Value |
| --------------- | ------ |
| `x-hasura-role` | `user` |

This header tells your API that the request is being made by someone with the role of `user`. That role's access-control
rules are then evaluated against your metadata resulting in whether or not that role has access to the requested
resource.

<Thumbnail
src="/img/get-started/0.0.1_console_disallowed_roles.png"
alt="Console with disallowed roles"
width="1000px"
/>

## What just happened?

We learned a lot!

First, we introduced you to a quick and easy way of adding custom business logic to your API. Hasura can host your
TypeScript connector for you, or you can host it yourself. You can learn more about the connector and deployment options
to Hasura DDN or self-hosted solutions by visiting [this page](/connectors/deployment).

Of course, we don't want any user accessing this command from our API. Just like our models, we can declaratively
restrict access to specific user roles using permissions. Find the... TODO
We saw that as this connector uses NodeJS under the hood, we can utilize any existing `npm` package for working with and
enriching data before returning it to our end users.

## Learn more
Finally, we learned that with the **superpower** of LSP-assisted metadata authoring, we can intuitively and
declaratively create relationships and permissions to use these functions in concert with models present in our
**supergraph** 🤯

Hasura can host your TypeScript connector for you, or you can host it yourself. You can learn more about the connector
and deployment options to Hasura DDN or self-hosted solutions by visiting [this page](/connectors/deployment).
For our final step in this getting started guide, let's add a second subgraph with a data source to truly make this a
supergraph.
Loading

0 comments on commit 214a67e

Please sign in to comment.