Running GraphQL using Apollo on SPA
Learn how to create a docker job and run GraphQL using Apollo on SPA.
Job Type
Select "Live" from the drop-down.
Job Name
You can include as many Docker jobs in a course as you need. The Job Name acts as an ID to each job. In this example, we are setting up a job named graph
.
Input File Name
In our Live VM functionality, this field can have any value in it. For demonstration purposes, let’s call it foo
.
Application Port
This is the port where your server will be listening. Port 3000 is always open by default and you can listen on one more port. Luckily, our server runs on 3000, so we’ll just provide that in the field.
Remember: If your application has a frontend, backend, etc., provide the frontend port in this field, and deploy the backend on port 3000.
HTTPS Strict Mode
If your server is HTTPS, tick this field.
Force Relaunch On Run
With this option enabled, the entire container shuts down and starts up again whenever Run is pressed in a widget where this job is selected.
Start Script
This script runs the first time you click the ‘RUN’ button in a SPA. It runs only once. Typically, this script would be used to ‘start’ the server.
The code present in the SPA widget’s editor is not actually part of our project. This code lives in a directory called /usercode, which is present in your container’s root directory by default.
So, if the user makes any changes to server.js
(which is in /usercode/project1/src
), they will not be reflected in your project’s server.js
(which is in your project folder).
That’s why you also need to copy the code from the /usercode
to your relevant folder. Here, we simply copy the code and use npm start
to start the server.
Your container is persistently active for 15 minutes. The Start Script is executed once again after the container times out.
Run Script
It runs every time you click the ‘RUN’ button. On the first run, the Run Script is executed before the Start Script, and after the first run, every subsequent run will only execute the commands written in this field (not the Start Script) if force relaunch on run is false. These commands are executed in a new terminal on the back end. This ensures that the Run Script is executed even if the terminal visible to you in the browser is held up by another process.
If you start the server using the Start Script; then the only thing you need to worry about is updating the code in the relevant files/folder as the updated code would lie in the file present in the /usercode as we discussed earlier in the Run Script section. That’s why It is typically used to update your project’s code in the container.
To read a detailed guide on the Single page application, click here.
Demo
Following is the demo of running GraphQL using Apollo studio in SPA.
We can test out the query by following the steps below:
-
Click “Run” on the widget below.
-
Wait for a few minutes to allow the server to create the public URL of your GraphQL server.
-
Click on the application URL to open the application, then click “Query your server”
-
Copy and paste the query bellow.
query getPizzas{pizzas{pizza}}
const apollo = require("apollo-server"); const { ApolloServer, gql } = apollo; // this is fake representation of database pizza topping record // on the production environment you probably want real data from database const pizzaToppings = [ {id: 1, topping: "Cheesy"} ] // this is fake representation of database record // on the production environment you probably want real data from database const pizzas = [ {id: 1, pizza: "Neapolitan Pizza", toppings: pizzaToppings, status: "AVAILABLE"}, {id: 2, pizza: "Chicago Pizza", toppings: pizzaToppings, status: "COOKING"}, {id: 3, pizza: "New York-Style Pizza", toppings: pizzaToppings, status: "UNAVAILABLE"}, {id: 4, pizza: "Sicilian Pizza", toppings: pizzaToppings, status: "AVAILABLE"}, {id: 5, pizza: "Greek Pizza", toppings: pizzaToppings, status: "COOKING"}, {id: 6, pizza: "California Pizza", toppings: pizzaToppings, status: "UNAVAILABLE"}, {id: 7, pizza: "Detroit Pizza", toppings: pizzaToppings, status: "COOKING"}, {id: 8, pizza: "St. Louis Pizza", toppings: pizzaToppings, status: "AVAILABLE"}, ] const server = new ApolloServer({ playground: true, typeDefs: gql` enum PizzaStatus { AVAILABLE, COOKING UNAVAILABLE } # Schema Type type Pizza { id: Int! pizza: String! stock: Int! toppings: [Topping!]! status: PizzaStatus! } type Topping { id: Int! topping: String! } input ToppingInput { id: Int! } type Query { pizzas(pizza: String, first: Int): [Pizza] pizza(id: Int): Pizza! } type Mutation { createPizza(pizza: String, toppings: [ToppingInput!]!): Pizza! updatePizza(id: Int!, pizza: String, toppings: [ToppingInput]): Pizza! } `, resolvers: { Query: { pizzas: (parent, args, context) => { const { name, first } = args; if(name){ // this is fake implementation of simple string matching // on production environment you would want to query from real database! return [pizzas.find(({pizza})=> pizza === name)]; } if(first){ return pizzas.slice(0, first) } return pizzas }, pizza: (parent, args, context) => { const { id } = args; if(id){ // this is fake implementation of simple id matching // on production environment you would want to query from real database! return pizzas.find(({id: pizzaId})=> pizzaId === id); } return undefined }, }, Mutation: { createPizza: (parent, args, context) => { // this is fake implementation of increment id of database // on production environment you would want to insert from real database! let { id } = pizzas.reduce((prev, curr) => prev.id > curr.id ? prev: curr) id = id + 1 // get pizza topping using pizza id const { toppings, pizza } = args; // treate topping as another table so you also need to get topping using current topping id! const toppingRecords = toppings.map(({id})=> pizzaToppings.find(({id: pizzaToppingId})=> pizzaToppingId === id)) const data = {id, toppings: toppingRecords, pizza} pizzas.push(data) return data; }, updatePizza: (parent, args, context) => { // get current pizza record using pizza id const { id, pizza, toppings } = args; const index = pizzas.findIndex((pizza) => pizza.id === id) // treate topping as another table so you also need to get topping using current topping id! const toppingRecords = toppings.map(({id})=> pizzaToppings.find(({id: pizzaToppingId})=> pizzaToppingId === id)) pizzas[index] = { id, toppings: toppingRecords, pizza} return pizzas[index]; }, }, }, }); server.listen({ port: 3000 }).then(({ url }) => { console.log(`Server running at ${url}`); });
The query above would return this as a response:
{
"data": {
"pizzas": [
{
"pizza": "Neapolitan Pizza"
},
{
"pizza": "Chicago Pizza"
},
{
"pizza": "New York-Style Pizza"
},
{
"pizza": "Sicilian Pizza"
},
{
"pizza": "Greek Pizza"
},
{
"pizza": "California Pizza"
},
{
"pizza": "Detroit Pizza"
},
{
"pizza": "St. Louis Pizza"
}
]
}
}