Dec 19, 2016 The easiest way to start using an API is by finding an HTTP client online, like REST-Client, Postman, or Paw. These ready-made (and often free) tools help you structure your requests to access existing APIs with the API key you received.
-->Build a Hosted Web App for the Microsoft Store with popular fullstack web technologies
This two-part tutorial provides a quick tour of modern fullstack web development as you build a simple memory game that works both in the browser and as a Hosted Web App for the Microsoft Store. In Part I you'll build a simple REST API service for the game's backend. By hosting the game logic in the cloud as an API service, you preserve the game state so your user can keep playing their same game instance across different devices. In Part II you'll build the front-end UI as a single-page web app with responsive layout.
We'll be using some of the most popular web technologies, including the Node.js runtime and Express for server-side development, the Bootstrap UI framework, the Pug template engine, and Swagger for building RESTful APIs. You'll also gain experience with the Azure Portal for cloud hosting and working with the Visual Studio Code editor.
Prerequisites
If you don't already have these resources on your machine, follow these download links:
- Node.js - Be sure to select the option to add Node to your PATH.
- Express generator- After you install Node, install Express by running
npm install express-generator -g
If you want to complete the final steps of hosting your API service and memory game app on Microsoft Azure, you'll need to create a free Azure account if you haven't already done so.
If you decide to bail on (or postpone) the Azure part, simply skip the final sections of parts I and II, which cover Azure hosting and packaging your app for the Microsoft Store. The API service and web app you build will still run locally (from
http://localhost:8000
and http://localhost:3000
, respectively) on your machine.Part I: Build a REST API backend
We'll first build a simple memory game API to power our memory game web app. We'll use Swagger to define our API and generate scaffolding code and a web UI for manual testing.
If you'd like to skip this part and move straight to Part II: Build a single-page web application, here's the finished code for Part I. Follow the README instructions to get the code up and running locally, or see 5. Host your API service on Azure and enable CORS to run it from Azure.
Game overview
Memory (also known as Concentration and Pelmanism), is a simple game consisting of a deck of card pairs. The cards are placed face-down on the table, and the player inspects the card values, two at a time, looking for matches. After each turn the cards are again placed face-down, unless a matching pair is found, in which case those two cards are cleared from the game. The game objective is to find all card pairs in the fewest amount of turns.
For instructional purposes, the game structure we'll use is very simple: it's single game, single player. However, the game logic is server-side (rather than on the client) to preserve the game state, so that you could keep playing the same game across different devices.
The data structure for a memory game consists simply of an array of JavaScript objects, each representing a single card, with indices in the array acting as card IDs. On the server, each card object has a value and a cleared flag. For example, a board of 2-matches (4 cards) might be randomly generated and serialized like this.
When the board array is passed to the client, value keys are removed from the array to prevent cheating (for example, inspecting the HTTP response body by using F12 browser tools). Here's how that same new game would look to a client calling the GET /game REST endpoint:
Speaking of endpoints, the memory game service will consist of three REST APIs.
POST /new
Initializes a new game board of the specified size (# of matches).
Parameter | Description |
---|---|
int size | Number of matching pairs to be shuffled into the game 'board'. Example: http://memorygameapisample/new?size=2 |
Response | Description |
---|---|
200 OK | New memory game of requested size is ready. |
400 BAD REQUEST | Requested size is outside of acceptable range. |
GET /game
Retrieves the current state of the memory game board.
No parameters
Response | Description |
---|---|
200 OK | Returns JSON array of card objects. Each card has a cleared property indicating whether its match has already been found. Matched cards also report their value. Example: [{'cleared':'false'},{'cleared':'false'},{'cleared':'true','value':1},{'cleared':'true','value':1}] |
PUT /guess
Specifies a card to reveal and checks for a match to the previously revealed card.
Parameter | Description |
---|---|
int card | Card ID (index in game board array) of the card to reveal. Each complete 'guess' consists of two specified cards (i.e., two calls to /guess with valid and unique card values). Example: http://memorygameapisample/guess?card=0 |
Response | Description |
---|---|
200 OK | Returns JSON with the id and value of the specified card. Example: [{'id':0,'value':1}] |
400 BAD REQUEST | Error with the specified card. See HTTP response body for further details. |
1. Spec out the API and generate code stubs
We'll use Swagger to transform the design of our memory game API into working Node.js server code. Here's how you might define our memory game APIs as Swagger metadata. We'll use this to generate server code stubs.
- Create a new folder (in your local GitHub directory, for example), and download the api.json file containing our memory game API definitions. Make sure your folder name doesn't contain any spaces.
- Open your favorite shell (or use Visual Studio Code's integrated terminal!) to that folder and run the following Node Package Manager (NPM) command to install the Yeoman (yo) code-scaffolding tool and Swagger generator for your global (-g) Node environment:
- Now we can generate the server scaffolding code by using Swagger:
- The swaggerize command will ask you several questions.
- Path (or URL) to swagger document: api.json
- Framework: express
- What would you like to call this project (YourFolderNameHere): [enter]
Answer everything else as you like; the information is mostly to supply the package.json file with your contact info so you can distribute your code as an NPM package. - Finally, install all the dependencies (listed in package.json) for your new project and Swagger UI support.Now start VS Code and File > Open Folder.., and move to the MemoryGameAPI directory. This is the Node.js API server you just created! It uses the popular ExpressJS web application framework to structure and run your project.
2. Customize the server code and setup debugging
The server.js file in your project root acts as the 'main' function of your server. Open it in VS Code and copy the following into it. Psp emulator pack. The lines modified from the generated code are commented with further explanation.
With that, it's time to run your server! Let's set up Visual Studio Code for Node debugging while we're at it. Select on the Debug panel icon (Ctrl+Shift+D) and then the gear icon (Open launch.json), and modify 'configurations' to this:
Now press F5 and open your browser to https://localhost:8000. The page should open to the Swagger UI for our memory game API, and from there you can expand the details and input fields for each of the methods. You can even try calling the APIs, although their responses will contain only mocked-up data (provided by the Swagmock module). It's time to add our game logic to make these APIs real.
3. Set up your route handlers
The Swagger file (configswagger.json) instructs our server how to handle various client HTTP requests by mapping each URL path it defines to a handler file (in handlers), and each method defined for that path (for example, GET, POST) to an
operationId
(function) within that handler file.In this layer of our program we'll add some input checking before passing the various client requests to our data model. Download (or copy and paste):
- This game.js code to your handlersgame.js file
- This guess.js code to your handlersguess.js file
- This new.js code to your handlersnew.js file
You can skim the comments in those files for more details about the changes, but in essence they check for basic input errors (for example, the client requests a new game with less than one match) and send descriptive error messages as needed. The handlers also route valid client requests through to their corresponding data files (in data) for further processing. Let's work on those next.
4. Set up your data model
It's time to swap out the placeholder the deck for a new game, identifying pairs of matched cards, and keeping track of game state. Copy and paste:
- This game.js code to your datagame.js file
- This guess.js code to your dataguess.js file
- This new.js code to your datanew.js file
For simplicity, we're storing our game board in a global variable (
global.board
) on our Node server. But realistically you'd use cloud storage (like Google Cloud Datastore or Azure DocumentDB) to make this into a viable memory-game API service that concurrently supports multiple games and players.Make sure you've saved all the changes in VS Code, fire up your server again (F5 in VS Code or
npm start
from shell, and then browse to https://localhost:8000) to test out the game API.Each time you press the Try it out! button on one of the /game, /guess, or /new operations, check the resulting Response Body and Response Code below to verify that everything's working as expected.
Try:
- Creating a new
size=2
game. - Guessing a couple of values.
- Checking the game board as the game progresses.
If everything looks good, your API service is ready to host on Azure! If you're running into problems, try commenting out the following lines in datagame.js.
With this change, the GET /game method will return all the card values (including the ones that haven't been cleared). This is a useful debug hack to keep in place as you build the front-end for the memory game.
5. (Optional) Host your API service on Azure and enable CORS
The Azure docs will walk you through:
- Setting up Git deployment for your API app, and
When registering your app, try to differentiate your App name (to avoid naming collisions with others requesting variations on the http://memorygameapi.azurewebsites.net URL).
If you've made it this far and Azure is now serving up your swagger UI, there's just one final step to the memory game backend. From Azure Portal, select your newly created App Service and then select or search for the CORS (Cross-Origin Resource Sharing) option. Under Allowed Origins, add an asterisk (
*
) and click Save. This lets you make cross-origin calls to your API service from your memory-game front-end as you develop it on your local machine. Once you finalize the memory-game front-end and deploy that to Azure, you can replace this entry with the specific URL of your web app.Going further
To make the memory game API a viable back-end service for a production app, you'll want to extend the code to support multiple players and games. For that you'll probably need to plumb in authentication (for managing player identities), a NoSQL database (for tracking games and players), and some basic unit testing for your API.
Here are some useful resources for going further:
Part II: Build a single-page web application
Now that you've built (or downloaded) the REST API backend from Part I, you're ready to create the single-page memory game front-end with Node, Express, and Bootstrap.
Part II of this tutorial will give you experience with:
- Node.js: to create the server hosting your game
- jQuery: a JavaScript library
- Express: for the web application framework
- Pug: (formerly Jade) for the templating engine
- Bootstrap: for the responsive layout
- Visual Studio Code: for code authoring, markdown viewing, and debugging
1. Create a Node.js application by using Express
Let's start by creating the Node.js project using Express.
- Open a command prompt and navigate to the directory where you want to store your game.
- Use the Express generator to create a new application called memory using the templating engine, Pug.
- In the memory directory, install the dependencies listed in the package.json file. The package.json file is created in the root of your project. This file contains the modules that are required for your Node.js app.After running this command, you should see a folder called node_modules that contains all of the modules needed for this app.
- Now, run your application.
- View your application by going to https://localhost:3000/.
- Change the default title of your web app by editing index.js in the memoryroutes directory. Change
Express
in the line below toMemory Game
(or another title of your choosing). - To refresh the app to see your new title, stop your app by pressing Crtl + C, Y in the command prompt, and then restart it with
npm start
.
2. Add client-side game logic code
You can find the files you need for this half of the tutorial in the Start folder. If you get lost, the finished code is available in the Final folder.
- Copy the scripts.js file inside of the Start folder and paste it in memorypublicjavascripts. This file contains all the game logic needed to run the game, including:
- Creating/starting a new game.
- Restoring a game stored on the server.
- Drawing the game board and cards based on user selection.
- Flipping the cards.
- In script.js, let's start by modifying the
newGame()
function. This function:- Handles the size of the game selection from the user.
- Fetches the gameboard array from the server.
- Calls the
drawGameBoard()
function to place the game board to the screen.
Add the following code inside ofnewGame()
after the// Add code from Part 2.2 here
comment.This code retrieves the value from the dropdown menu withid='selectGameSize'
(which we will create later) and stores it in a variable (size
). We use theparseInt()
function to parse the string value from the dropdown to return an integer, so we can pass thesize
of the requested game board to the server.We use the/new
method created in Part I of this tutorial to post the chosen size of the game board to the server. The method returns a single JSON array of cards andtrue/false
values indicating whether the cards have been matched. - Next, fill in the
restoreGame()
function that restores the last game played. For simplicity's sake, the app always loads the last game played. If there is not a game stored on the server, use the drop-down menu to start a new game.Copy and paste this code intorestoreGame()
.The game will now fetch the game state from the server. For more information about the/game
method being used in this step, see Part I of this tutorial. If you are using Azure (or another service) to host the backend API, replace the localhost address above with your production URL. - Now we want to create the
drawGameBoard()
function. This function:- Detects the size of the game and applies specific CSS styles.
- Generates the cards in HTML.
- Adds the cards to the page.
Copy and paste this code into thedrawGameBoard()
function in scripts.js: - Next, we need to complete the
flipCard()
function. This function handles the majority of the game logic, including getting the values of the selected cards from the server by using the/guess
method developed in Part I of the tutorial. Don't forget to replace the localhost address with your production URL if you are cloud hosting the REST API backend.In theflipCard()
function, uncomment this code:
Tip
If you're using Visual Studio Code, select all the lines of code you wish to uncomment, and press Crtl + K, U
Here we use
jQuery.ajax()
and the PUT/guess
method created in Part I.This code executes in the following order.
- The
id
of the first card the user selected is added as the first value to the selectedCards[] array:selectedCards[0]
- The value (
id
) inselectedCards[0]
is posted to the server using the/guess
method - The server responds with the
value
of that card (an integer) - A Bootstrap glyphicon is added to the back of the card whose
id
isselectedCards[0]
- The first card's
value
(from the server) is stored in theselectedCardsValues[]
array:selectedCardsValues[0]
.
The user's second guess follows the same logic. If the cards that the user selected have the same IDs, (for example,
selectedCards[0] selectedCards[1]
), the cards are a match! The CSS class .matched
is added to the matched cards (turning them green) and the cards remained flipped.Now we need to add logic to check whether the user matched all of the cards and won the game. Inside of the
flipCard()
function, add the following lines of code under the //check if the user won the game
comment.If the number of cards flipped matches the size of the game board (for example,
cardsFlipped gameBoardSize
), there are no more cards to flip and the user has won the game. We'll add some simple HTML to the div
with id='game-board'
to let the user know they won and can play again.3. Create the user interface
Now let's see all this code in action by creating the user interface. In this tutorial, we use the templating engine Pug (formally Jade). Pug is clean, whitespace-sensitive syntax for writing HTML. Here's an example.
becomes
- Replace the layout.pug file in memoryviews with the provided layout.pug file in the Start folder. Inside of layout.pug, you'll see links to:
- Bootstrap
- jQuery
- A custom CSS file
- The JavaScript file we just finished modifying
- Open the file named index.pug in the directory memoryviews.This file extends the layout.pug file and will render our game. Inside of layout.pug, paste the following lines of code:
Tip
Remember: Pug is whitespace sensitive. Make sure all of your indentations are correct!
4. Use Bootstrap's grid system to create a responsive layout
Bootstrap's grid system is a fluid grid system that scales a grid as a device's viewport changes. The cards in this game use Bootstrap's predefined grid system classes for the layout, including:
.container-fluid
: specifies the fluid container for the grid.row-fluid
: specifies the fluid rows.col-xs-3
: specifies the number of columns
Bootstrap's grid system allows a grid system to collapse into one vertical column, like you would see on a navigation menu on a mobile device. However, because we want our game always to have columns, we use the predefined class
.col-xs-3
, which keeps the grid horizontal at all times.The grid system allows up to 12 columns. Since we only want 4 columns in our game, we use the class
.col-xs-3
. This class specifies that we need each of our columns to span the width of 3 of the 12 available columns mentioned before. This image shows a 12-column grid and a 4-column grid, like the one used in this game.- Open scripts.js and find the
drawGameBoard()
function. In the block of code where we generate the HTML for each card, can you spot thediv
element withclass='col-xs-3'
? - Inside of index.pug, let's add the predefined Bootstrap classes mentioned previously to create our fluid layout. Change index.pug to the following.
5. Add a card-flip animation with CSS Transforms
Replace the style.css file in memorypublicstylesheets with the style.css file from the Start folder.
Adding a flip motion using CSS Transforms gives the cards a realistic, 3D flipping motion. The cards in the game are created by using the following HTML structure and programmatically added to the game board (in the
drawGameBoard()
function shown previously).- To start, give perspective to the parent container of the animation (
.flipContainer
). This gives the illusion of depth for its child elements: the higher the value, the farther away from the user the element will appear. Let's add the following perspective to the.flipContainer
class in style.css. - Now add the following properties to the
.cards
class in style.css. The.cards
div
is the element that will actually be doing the flipping animation, showing either the front or the back of the card.Thetransform-style
property establishes a 3D-rendering context, and the children of the.cards
class (.front
and.back
are members of the 3D space. Adding thetransition-duration
property specifies the number of seconds for the animation to finish. - Using the
transform
property, we can rotate the card around the Y-axis. Add the following CSS tocards.flip
.The style defined incards.flip
is toggled on and off in theflipCard
function by using.toggleClass()
.Now when a user clicks on a card, the card is rotated 180 degrees.
6. Test and play
Macos Tool For Interacting With Rest Api App
Congratulations! You've finished creating the web app! Let's test it.
- Open a command prompt in your memory directory and enter the following command:
npm start
- In your browser, go to https://localhost:3000/ and play a game!
- If you encounter any errors, you can use Visual Studio Code's Node.js debugging tools by pressing F5 on your keyboard and typing
Node.js
. For more information about debugging in Visual Studio Code, check out this article.You can also compare your code to the code provided in the Final folder. - To stop the game, in the command prompt type: Ctrl + C, Y.
Going further
You can now deploy your app to Azure (or any other cloud hosting service) for testing across different device form factors, such as mobile, tablet, and desktop. (Don't forgot to test across different browsers too!) Once your app is ready for production, you can easily package it as a Hosted Web App (HWA) for the Universal Windows Platform (UWP) and distribute it from the Microsoft Store.
The basic steps for publishing to the Microsoft Store are:
- Create a Windows Developer account
- Use the app submission checklist
- Submit your app for certification
Here are some useful resources for going further: