Extend a Model
Introduction
In this tutorial, you'll learn how to extend an existing field on a model to enhance its functionality. We'll demonstrate this using PostgreSQL as the data source, but the steps apply to any data source supported by Hasura DDN. By the end, you'll have created a relationship that integrates custom logic with your API. This process will show you how to:
- Initialize a DDN project and connect to a data source.
- Add a model to your metadata from a database table.
- Create and implement custom logic in a lambda connector.
- Establish relationships between models and custom commands.
This tutorial should take about ten minutes.
Step 1. Initialize a new local DDN project
ddn supergraph init lambda-tutorial
Step 2. Prepare the PostgreSQL data
ddn connector init my_pg -i
From the dropdown, start typing PostgreSQL
and hit enter to advance through all the options.
The CLI will output something similar to this:
HINT To access the local Postgres database:
- Run: docker compose -f app/connector/my_pg/compose.postgres-adminer.yaml up -d
- Open Adminer in your browser at http://localhost:5143 and create tables
- To connect to the database using other clients use postgresql://user:[email protected]:8105/dev
docker compose -f app/connector/my_pg/compose.postgres-adminer.yaml up -d
Run docker ps
to see on which port Adminer is running. Then, you can navigate to the address below to access it:
http://localhost:<ADMINER_PORT>
--- Create the table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT NOT NULL
);
--- Insert some data
INSERT INTO users (name, age) VALUES ('Alice', 25);
INSERT INTO users (name, age) VALUES ('Bob', 30);
INSERT INTO users (name, age) VALUES ('Charlie', 35);
You can verify this worked by using Adminer to query all records from the users
table:
SELECT * FROM users;
ddn connector introspect my_pg
ddn models add my_pg users
Open the app/metadata
directory and you'll find a newly-generated file: Users.hml
. The DDN CLI will use this Hasura
Metadata Language file to represent the users
table from PostgreSQL in your API as a
model.
Step 3. Initialize the lambda connector
- TypeScript
- Python
- Go
ddn connector init my_ts -i
- Select
hasura/nodejs
from the list of connectors. - Choose a port (press enter to accept the default recommended by the CLI).
If you open the app/connector/my_ts
directory, you'll see the functions.ts
file generated by the CLI; this will be
the entrypoint for your connector.
ddn connector init my_python -i
- Select
hasura/python
from the list of connectors. - Choose a port (press enter to accept the default recommended by the CLI).
If you open the app/connector/my_python
directory, you'll see the functions.py
file generated by the CLI; this will
be the entrypoint for your connector.
ddn connector init my_go -i
- Select
hasura/go
from the list of connectors. - Choose a port (press enter to accept the default recommended by the CLI).
If you open the app/connector/my_go
directory, you'll see Go files in the functions
folder; these will serve as the
entrypoint for your connector.
Step 4. Add your custom logic
- TypeScript
- Python
- Go
cd app/connector/my_ts && npm install
/**
* @readonly
*/
export function shoutName(name: string) {
return `${name.toUpperCase()}`;
}
from hasura_ndc import start
from hasura_ndc.function_connector import FunctionConnector
from pydantic import BaseModel, Field
from hasura_ndc.errors import UnprocessableContent
from typing import Annotated
connector = FunctionConnector()
@connector.register_query
def shoutName(name: str) -> str:
return f"{name}".upper()
if __name__ == "__main__":
start(connector)
package functions
import (
"context"
"strings"
"hasura-ndc.dev/ndc-go/types"
)
// UppercaseNameArguments represents the input arguments for converting a Name to uppercase.
type UppercaseNameArguments struct {
Name string `json:"Name"`
}
// FunctionShoutName converts the Name to uppercase and returns it as a string.
func FunctionShoutName(ctx context.Context, state *types.State, arguments *UppercaseNameArguments) (string, error) {
uppercase := strings.ToUpper(arguments.Name)
return uppercase, nil
}
Step 5. Introspect your lambda connector
- TypeScript
- Python
- Go
ddn connector introspect my_ts
# alternatively, use ddn command add my_ts "*" for bulk adds
ddn command add my_ts shoutName
ddn connector introspect my_python
# alternatively, use ddn command add my_python "*" for bulk adds
ddn command add my_python shoutName
ddn connector introspect my_go
# alternatively, use ddn command add my_go "*" for bulk adds
ddn command add my_go shoutName
Step 6. Create a relationship
---
kind: Relationship
version: v1
definition:
name: shoutName # Define a name to expose in the supergraph API
sourceType: Users # The existing source object type (which also defines the source model Users)
target:
command: # The target is a command
name: ShoutName # The name of the existing command we have defined in metadata
subgraph: app # The existing subgraph the command is defined in
mapping:
- source:
fieldPath:
- fieldName: name # The field on the source object type that we want to provide to the target command as an argument
target:
argument:
argumentName: name # The name of the argument on the target command that we want to map to the source field
Step 7. Create a new build and test
ddn supergraph build local
ddn run docker-start
ddn console --local
query UsersWithShoutedName {
users {
id
name
shoutName
}
}
Next steps
Now that you know how to extend your existing data sources using custom business logic, check out our advanced use cases in this section: