Shopify Backend Challenge 2019
Explore mini-market docs »
Demo it now »
To get the Rails server running locally:
- Clone the repository
bundle install
to install required dependenciesrails db:migrate
to build database migrations- For mock data, use
rails db:seed
rails s
to start the server- Visit
http://localhost:3000/
to access the demo
cd
into the project directory- Run tests using
rails test
Unlike a RESTFul API, mini-market has a single endpoint:
POST https://mini-market.zhao.io/graphql
Using the demo interface, you can follow executing some commands below:
Example Request:
{
products(onlyAvailable: true) {
id
title
price
inventoryCount
}
}
Response:
{
"data": {
"products": [
{
"id": "1",
"title": "Iron Helmet",
"price": 79.99,
"inventoryCount": 25
},
{
"id": "2",
"title": "Hylian Shield",
"price": 1333.37,
"inventoryCount": 1
},
{
"id": "3",
"title": "Leather Boots",
"price": 16.99,
"inventoryCount": 10
}
]
}
}
Argument | Type | Default | Description |
---|---|---|---|
onlyAvailable | Boolean | false |
Show only products with available inventory |
Example Request:
{
cart {
cartItems {
product{
title
}
price
quantity
}
}
}
Response:
{
"data": {
"cart": {
"cartItems": [
{
"product": {
"title": "Iron Helmet"
},
"price": 79.99,
"quantity": 3
}
]
}
}
}
Similarly to how Shopify handles mutation errors, you should always be including userErrors
in your mutation queries. Otherwise, if an error occurs, it's likely you won't see it without it.
Example Request:
mutation {
addToCart(productId: 1, quantity: 3) {
cart{
cartItems {
product {
title
}
price
quantity
}
subtotal
}
userErrors {
message
path
}
}
}
Response:
{
"data": {
"addToCart": {
"cart": {
"cartItems": [
{
"product": {
"title": "Iron Helmet"
},
"price": 79.99,
"quantity": 3
}
],
"subtotal": 239.97
},
"userErrors": []
}
}
}
Argument | Type | Default | Description |
---|---|---|---|
productId | Integer | Product ID of the item to be added | |
quantity | Integer | Number of items to be added |
Checkout the current session's cart. You must be be logged in before performing this action.
Example Request:
mutation {
checkoutCart {
receipt {
receiptItems {
price
product {
title
}
quantity
}
subtotal
}
userErrors {
message
path
}
}
}
Response:
{
"data": {
"checkoutCart": {
"receipt": {
"receiptItems": [
{
"price": 79.99,
"product": {
"title": "Iron Helmet"
},
"quantity": 3
}
],
"subtotal": 239.97
},
"userErrors": []
}
}
}
Due to the limitations of this project, tokens are currently stored in the session. In practice, this should be avoided in favour of JWT and other stateless authentication methods.
Before you are able to checkout your cart, you must be logged in and authenticated.
Example Request:
mutation {
createUser(name: "Steven Z.", email: "steven@zhao.io", password: "hunter2") {
user {
id
}
userErrors {
message
path
}
}
}
Response:
{
"data": {
"createUser": {
"user": {
"id": "1"
},
"userErrors": []
}
}
}
Once you have an account, you must sign-in to generate an authentication token.
Example Request:
mutation {
signInUser(email: "steven@zhao.io", password: "hunter2") {
token
}
}
Response:
{
"data": {
"signInUser": {
"token": "TI3MuWQMvB37N/xls/CqXeTrvw==--dJwQyWh1WW+ATSIT--+eAMWr6FZxx31CQ3GqonLw=="
}
}
}
This project was a great learning experience. I had nil experience working with Ruby/Rails nor GraphQL prior to this challenge. Takeaways:
- Ruby is very idiomatic. It's simple and beautiful when it works. I spent a lot of time researching best practices before I dove in and was always surprised how readable Ruby can get. I will need to improve on this front.
- Juggling between Rails/GraphQL types was daunting at first, but now I really appreciate how well they work together.
- Linting saves lives! Rubocop helped guide me away from taking practices from other languages over when I first started out.