Skip to content

Instantly share code, notes, and snippets.

@ajcwebdev
Last active Dec 21, 2021
Embed
What would you like to do?
HorseZen

HorseZen

StepZen provides different custom directives for connecting to various kinds of data sources such as the @rest directive for connecting to REST endpoints and the @graphql directive for connecting to GraphQL APIs. We will use these to connect to entirely way too many data sources.

Outline

Create Project

mkdir -p horsezen/schema
cd horsezen
touch index.graphql \
  schema/test.graphql schema/test.graphql \
  schema/jsonplaceholder.graphql schema/catbreed.graphql \
  schema/books.graphql schema/characters.graphql \
  schema/stores.graphql schema/users.graphql
echo 'config.yaml\nnode_modules\n.DS_Store\n.env' > .gitignore
echo '{"endpoint": "api/horsezen"}' > stepzen.config.json
echo 'configurationset:' > config.yaml

Every StepZen project requires an index.graphql that ties together all of our schemas. index.graphql tells StepZen what files hold your types and queries.

schema @sdl(
  files: [
    "schema/test.graphql"
    "schema/jsonplaceholder.graphql"
    "schema/catbreed.graphql"
    "schema/books.graphql"
    "schema/characters.graphql"
    "schema/countries.graphql"
    "schema/posts.graphql"
    "schema/stores.graphql"
    "schema/users.graphql"
  ]
) {
  query: Query
}

Run stepzen start

stepzen start will open a local development server on localhost:5001.

stepzen start

Mock JSON Data

Mock JSON Schema

The Test interface includes a named type for each specified type. The getTest query returns a single Test object.

# schema/test.graphql

interface Test {
  String: String!
  Int: Int!
  Float: Float!
  JSON: JSON!
  JSON2: JSON!
  Date: Date!
  DateTime: DateTime!
}

type Query {
  getTest: Test
}

Mock JSON Query

The MOCK_JSON query runs the getTest query and returns a Test with all of the specified types.

query MOCK_JSON {
  getTest {
    Date
    DateTime
    Float
    Int
    JSON
    JSON2
    String
  }
}

REST APIs

@rest is a custom StepZen directive that connects you to a REST API. It supports PUT, POST and GET http methods.

JSON Placeholder Schema

# schema/jsonplaceholder.graphql

type User {
  id: ID!
  name: String!
  username: String!
  email: String!
  phone: String!
  website: String!
}

type Query {
  getMockUsers: [User]
    @rest(
      endpoint:"https://jsonplaceholder.typicode.com/users"
    )
}

JSON Placeholder Query

query GET_MOCK_USERS {
  getMockUsers {
    id
    name
    username
    email
    phone
    website
  }
}

Rick and Morty GraphQL API

Connect to the public Rick and Morty GraphQL API with the @graphql directive.

Rick and Morty Schema

To match the Rick and Morty API schema, return a results array containing the Character objects and call the type Characters. The Character object has three types:

  • id with the type ID
  • name for the character's name with the type String
  • image of the character with the type String. The image is a URL to a .jpeg file contained within the string.
# schema/characters.graphql

type Character {
  id: ID
  name: String
  image: String
}

type Characters {
  results: [Character]
}

type Query {
  characters: Characters
    @graphql(
      endpoint: "https://rickandmortyapi.com/graphql"
    )
}

Our Query type has a characters query that returns the Characters, as specified in our previous types. The @graphql directive takes the endpoint of the GraphQL API, which in this case is https://rickandmortyapi.com/graphql with the URL placed in between quotation marks.

Characters Query

To test our new endpoint, run the following query called CHARACTERS_QUERY. This executes the characters query and returns an array of Character objects with their id, name, and image.

query CHARACTERS_QUERY {
  characters {
    results {
      id
      name
      image
    }
  }
}

Cat API

We'll be using the Cat API to get a breed by ID. Our schema creates a Breed type with id, name, temperament, and life_span.

Cat API Schema

# schema/catbreed.graphql

type Breed {
  id: String!
  name: String!
  temperament: String!
  life_span: String!
  origin: String!
}

type Query {
  breedById(id: String!): [Breed]
    @rest(
      endpoint: "https://api.thecatapi.com/v1/breeds/search?q=$id"
      configuration: "cat_config"
    )
}

endpoint tells StepZen what endpoint to call. You can see the variable $id is set with a dollar sign in the url. configuration refers to your configuation setting in config.yaml.

Cat API Configuration

configurationset:
  - configuration:
      name: cat_config
      Authorization: Apikey MY_PERSONAL_ACCESS_TOKEN

Cat API Query

Run BREED_QUERY with the abys for the id.

query BREED_QUERY {
  breedById(id: "abys") {
    id
    life_span
    name
    temperament
    origin
  }
}

PostgreSQL Database with Supabase

Supabase is a base for your data that's super. That base is PostgreSQL.

Supabase Configuration

configurationset:
  - configuration:
      name: supabase_config
      apikey: xxxx

Create Supabase Database

Select new project.

01-supabase-dashboard-new-project

After the project has been created, scroll down to see instructions for connecting to your database.

02-connecting-to-your-project

Click "Create a new table" in the Table Editor. Create a Books Table and add a name column with a text type.

Books Schema

Book type with types for id, name, created_at, and updated_at. The books query returns an array of Book objects. The createBook mutation takes the name of a book as an argument.

# schema/books.graphql

type Book {
  id: ID!
  name: String!
  created_at: DateTime!
}

type Query {
  books: [Book]
    @rest(
      endpoint: "https://xxxx.supabase.co/rest/v1/books",
      configuration: "supabase_config",
      headers: [{ name: "apikey" value: "$apikey" }]
    )
}

type Mutation {
  createBook(name: String!): Book
    @rest(
      endpoint: "https://xxxx.supabase.co/rest/v1/books"
      configuration: "supabase_config"
      headers: [{ name: "apikey" value: "$apikey" }]
      method: POST
      postbody: "{\"name\": \"{{.Get \"name\"}}\"}"
    )
}

Supabase Queries

Create a book with a GraphQL mutation.

mutation CREATE_BOOK {
  createBook(name: "stepzen-book-test") {
    id
    name
  }
}

Return all the books.

query BOOKS_QUERY {
  books {
    id
    name
    created_at
  }
}

Fauna

Fauna is another database for the data that couldn't go in your other bases.

type Store {
  _id: ID!
  name: String!
}

type StorePage {
  data: [Store]!
  after: String
  before: String
}

input StoreInput {
  name: String!
}

type Query {
  findStoreByID(id: ID!): Store
    @graphql(
      endpoint: "https://graphql.fauna.com/graphql"
      configuration: "fauna_config"
    )
  allStores: StorePage!
    @graphql(
      endpoint: "https://graphql.fauna.com/graphql"
      configuration: "fauna_config"
    )
}

type Mutation {
  createStore(data: StoreInput!): Store!
    @graphql(
      endpoint: "https://graphql.fauna.com/graphql"
      configuration: "fauna_config"
    )
  updateStore(id: ID!, data: StoreInput!): Store
    @graphql(
      endpoint: "https://graphql.fauna.com/graphql"
      configuration: "fauna_config"
    )
  deleteStore(id: ID!): Store
    @graphql(
      endpoint: "https://graphql.fauna.com/graphql"
      configuration: "fauna_config"
    )
}

Fauna Configuration

  - configuration:
      name: fauna_config
      Authorization: Basic MY_FAUNA_KEY

Run CREATE_STORE and then FIND_STORE_BY_ID with the returned _id.

mutation CREATE_STORE {
  createStore(data: {
    name: "Fake Store",
  }) {
    name
    _id
  }
}
query FIND_STORE_BY_ID {
  findStoreByID(id: "") {
    _id
    name
  }
}

Redwood API with Netlify Functions

yarn create redwood-app horsezen-redwood
cd horsezen-redwood

Create Users schema in prisma.schema.

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider      = "prisma-client-js"
  binaryTargets = "native"
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
}

Provision a PostgreSQL database with Railway and then set up the database with Prisma Migrate and the scaffold command.

yarn rw prisma migrate dev --name users
yarn rw g scaffold user
yarn rw dev

Setup Netlify Deploy.

yarn rw setup deploy netlify

Create a blank repository at repo.new. Push the project to GitHub and connect your repo to Netlify.

git init
git add .
git commit -m "users"
git remote add origin https://github.com/ajcwebdev/horsezen-redwood.git
git push -u origin main

users.graphql contains a User type and users query.

# schema/users.graphql

type User {
  id: Int!
  name: String!
}

type Query {
  users: [User!]!
    @graphql(
      endpoint:"https://stepzen-redwood-users.netlify.app/.netlify/functions/graphql"
    )
}
query USERS_QUERY {
  users {
    id
    name
  }
}

Final Configuration

configurationset:
  - configuration:
      name: cat_config
      Authorization: Apikey xxxx
  - configuration:
      name: supabase_config
      apikey: xxxx
  - configuration:
      name: fauna_config
      Authorization: Basic xxxx

Mega Query

query MEGA_QUERY {
  getTest {
    Date
    DateTime
    Float
    Int
    JSON
    JSON2
    String
  }
  getMockUsers {
    id
    name
    username
    email
    phone
    website
  }
  characters {
    results {
      id
      name
      image
    }
  }
  breedById(id: "abys") {
    id
    life_span
    name
    temperament
    origin
  }
  books {
    id
    name
    created_at
  }
  findStoreByID(id: "318563488185188943") {
    _id
    name
  }
  users {
    id
    name
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment