An open API service indexing awesome lists of open source software.

https://github.com/stepzen-dev/stepzen-redwood

A StepZen application with RedwoodJS and Shopify
https://github.com/stepzen-dev/stepzen-redwood

Last synced: 11 months ago
JSON representation

A StepZen application with RedwoodJS and Shopify

Awesome Lists containing this project

README

          

# Redwood+StepZen and Shopify

![stepzen-redwood-cover](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gmoeed12g8imj1oz4dd.png)

[RedwoodJS](http://redwoodjs.com/) is a fullstack, serverless web application framework for building and deploying Jamstack applications. Imagine a React frontend, statically delivered from a CDN, talking via GraphQL to a backend running on AWS Lambdas, all deployable with `git push`.

This example project creates a GraphQL API for a Shopify backend connected to a React frontend and deployed on a static hosting provider. Redwood's `api` side is auto-configured with a GraphQL handler that can be deployed with serverless functions, enabling [secure API routes](https://stepzen.com/blog/how-to-secure-api-routes-for-jamstack-sites).

## Project Structure

```
├── api
│ ├── src
│ │ ├── functions
│ │ │ └── graphql.js
│ │ ├── graphql
│ │ │ └── products.sdl.js
│ │ ├── lib
│ │ │ └── client.js
│ │ └── services
│ │ └── products
│ │ └── products.js
│ └── stepzen
│ ├── shopify
│ │ └── products.graphql
│ └── index.graphql
└── web
├── public
│ ├── README.md
│ ├── favicon.png
│ └── robots.txt
└── src
├── components
│ └── ProductsCell
│ ├── ProductsCell.js
│ ├── ProductsCell.mock.js
│ ├── ProductsCell.stories.js
│ └── ProductsCell.test.js
├── layouts
├── pages
│ ├── FatalErrorPage
│ │ └── FatalErrorPage.js
│ ├── HomePage
│ │ └── HomePage.js
│ └── NotFoundPage
│ └── NotFoundPage.js
├── App.js
├── Routes.js
├── index.css
└── index.html
```

## Setup

### Clone repo

```bash
git clone https://github.com/stepzen-samples/stepzen-redwood.git
cd stepzen-redwood
```

### Install dependencies

We'll use [yarn](https://classic.yarnpkg.com/en/docs/install) (which is a requirement) to install our dependencies.

```bash
yarn
```

### Fire it up

Start the development server with `yarn rw dev`.

```bash
yarn rw dev
```

Your browser should open automatically to `http://localhost:8910` to see the web app.

![01-empty-products](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c1bzc8dkbxmtpwo1uv5m.png)

### `config.yaml`

We don't have any products because we haven't connected to Shopify yet. `config.yaml` contains the keys and other credential information that StepZen needs to access your backend data sources. Leave your development server running and open a new terminal.

```bash
touch api/stepzen/config.yaml
```

To connect your Shopify account, enter your `api_key`, `api_password`, and `store_name`.

```yaml
configurationset:
- configuration:
name: shopify_config
api_key:
api_password:
store_name:
```

### Deploy endpoint

Deploy your endpoint with `stepzen start`.

```bash
stepzen start
```

In addition to deploying your endpoint, it will also be running on `localhost:5000`.

```bash
Watching ~/stepzen-redwood/api/stepzen for GraphQL changes

http://localhost:5000/stepzen-redwood/products
```

The terminal will output the link to a deployed endpoint you can query. Your own account name will replace `biggs`.

```bash
Deploying to StepZen...... done

Successfully deployed stepzen-redwood/products at 2:33:27 PM

Your endpoint is available at https://biggs.stepzen.net/stepzen-redwood/products/__graphql
```

### Query endpoint

`getProducts` returns an array of `Product` objects with the `title`, `id`, and `handle` for each. Open the GraphQL explorer and enter the following query.

```graphql
query getProducts {
products {
title
id
handle
}
}
```

![03-stepzen-graphiql-editor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qhbqznno6y5x9kghzy55.png)

### Create `.env` file

Go back to your original terminal and turn off the development server (⌃C). We need to restart the server because we are going to create a `.env` file to hold our environment variables.

```bash
touch .env
```

The server does not detect this change and will result in an error message if you do not restart it after creating the `.env` file.

```bash
API_ENDPOINT=
API_KEY=
```

Enter the endpoint generated by `stepzen start` and include your `API_KEY` which can be found in your StepZen account dashboard.

### Start the development server again

```bash
yarn rw dev
```

The `web` side will be running again on `localhost:8910` and now will return products.

![05-home-page-localhost](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gj4pvqmrlil8wwsewlqr.png)

The `api` side can be accessed through a GraphiQL explorer running on `localhost:8911/graphql`.

![04-redwood-api-graphiql-editor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw0tmstq7r5wl4xx41dy.png)

## StepZen Side

`products.graphql` has a `Product` type and a `products` query that returns an array of `Product` objects. The `@rest` directive accepts the `endpoint` from Shopify and the `@supplies` directive provides the query.

```graphql
# api/stepzen/shopify/products.graphql

type Product {
id: ID!
handle: String
title: String
}

type Query {
products: [Product]
@rest(
resultroot: "products[]"
endpoint: "https://$api_key:$api_password@$store_name.myshopify.com/admin/api/2020-01/products.json"
configuration: "shopify_config"
)
}
```

### index.graphql

Our `schema` in `index.graphql` ties together all of our other schemas. For this example we just have the `products.graphql` file included in our `@sdl` directive.

```graphql
# api/stepzen/index.graphql

schema
@sdl(
files: [ "schema/products.graphql" ]
) {
query: Query
}
```

This file tells StepZen how to assemble the various type definition files into a complete GraphQL schema. The `@sdl` directive is a StepZen directive that specifies the list of files to assemble.

## Redwood API Side

The `functions` directory contains a `graphql.js` file that defines our GraphQL handler for serverless deployment to AWS Lambda or platforms that offer Lambda support such as Netlify and Vercel.

```javascript
// api/src/functions/graphql.js

import {
createGraphQLHandler,
makeMergedSchema,
makeServices,
} from '@redwoodjs/api'

import schemas from 'src/graphql/**/*.{js,ts}'
import services from 'src/services/**/*.{js,ts}'

export const handler = createGraphQLHandler({
schema: makeMergedSchema({
schemas,
services: makeServices({ services }),
}),
})
```

### Products Schema

The `graphql` directory contains `products.sdl.js` with your GraphQL schema written in a Schema Definition Language. This ensures that our Redwood API will have a `schema` that matches our `schema` in `products.graphql`.

```javascript
// api/src/graphql/products.sdl.js

export const schema = gql`
type Product {
id: ID
handle: String
title: String
}

type Query {
products: [Product]
}
`
```

The schema includes a `Product` type, and each `Product` has an `id`, `handle`, and `title`. The `products` query returns an array of `Product` objects.

### GraphQL Client

While Redwood's `web` side includes Apollo Client by default, its `api` side does not include any built in mechanism for making HTTP requests.

```javascript
// api/src/lib/client.js

import { GraphQLClient } from 'graphql-request'

export const request = async (query = {}) => {
const endpoint = process.env.API_ENDPOINT

const graphQLClient = new GraphQLClient(endpoint, {
headers: {
authorization: 'apikey ' + process.env.API_KEY
},
})
try {
return await graphQLClient.request(query)
} catch (err) {
console.log(err)
return err
}
}
```

This project uses `graphql-request` but `node-fetch` could be used instead.
* `endpoint` is set to the url generated by `stepzen start`
* `authorization` includes your StepZen API key appended to `apikey `

### Products Service

In the `services` directory we have a `products` directory with a `products.js` service.

```javascript
// api/src/services/products/products.js

import { request } from 'src/lib/client'
import { gql } from 'graphql-request'

export const products = async () => {
const GET_PRODUCTS_QUERY = gql`
query getProducts {
products {
title
id
handle
}
}
`

const data = await request(GET_PRODUCTS_QUERY)

return data['products']
}
```

The query is asking for the list of `products` and their `title`, `id`, and `handle`. The `GraphQLClient` is imported from `src/lib/client`.

## Redwood Web Side

The `web` side contains our `ProductsCell` for fetching `products` and a `HomePage` for rendering the cell.

### ProductsCell

`getProducts` returns the `id`, `title`, and `handle` of each `Product`. This will send the query to our `api` side, which in turn sends a query to our StepZen API.

```jsx
// web/src/components/ProductsCell/ProductsCell.js

export const QUERY = gql`
query getProducts {
products {
id
title
handle
}
}
`

export const Loading = () =>

Almost there...

export const Empty = () =>
WE NEED PRODUCTS

export const Failure = ({ error }) =>
{error.message}

export const Success = ({ products }) => {
return (


    {products.map(product => (
  • {product.title}

  • ))}

)
}
```

### HomePage

`ProductsCell` is imported onto `HomePage` to display the information fetched by the cell's query.

```jsx
// web/src/pages/HomePage/HomePage.js

import ProductsCell from 'src/components/ProductsCell'

const HomePage = () => {
return (
<>

StepZen+Redwood and Shopify


Products


>
)
}

export default HomePage
```

## Deploy to Netlify

Redwood provides helpful setup commands to deploy to various hosting providers. Our project is already configured to deploy to [Netlify](https://redwoodjs.com/docs/deploy#netlify-deploy) with our `netlify.toml` file.

```toml
[build]
command = "yarn rw deploy netlify"
publish = "web/dist"
functions = "api/dist/functions"

[dev]
command = "yarn rw dev"

[[redirects]]
from = "/*"
to = "/index.html"
status = 200
```

The `netlify.toml` file lets Netlify know that:
* Your `build` command is `yarn rw deploy netlify`
* The `publish` directory for your assets is `web/dist`
* Your `functions` will be in `api/dist/functions`

![06-home-page-hosted-on-netlify](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/054g4nmv18e9vbqte86a.png)

Open your browser's developer tools and look at the response in your Network tab.

![07-console-log-response-data](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kkobxqb731w0j7k3nw01.png)