🏠 Table of Contents > 📚 What is DataStax Astra and Stargate > ⚒️ Connect to your Astra database
Objectives
The REST API is stateless
, and therefore helps functions scale horizontally.
In step 2 of the Battlestax tutorial, we will:
- Create an Astra database to store game documents
- Create test cases to check that our API call is working correctly
- Build the API call to Astra to create a game document, based on the requirements from our test
We will cover:
- Create your Astra instance
- Setup your environment
- Creating the
insertGame
Netlify endpoint - Connect to Astra
- Hook it all together
- Running TDD tests
One of the first things we need to do is hook up the "plumbing" of our application to talk to our back-end services, namely, our Cassandra database with Astra and Netlify. Once this is in place, we are connected and ready to go with the services we need to power our app.
We will also be making use of the Document API to connect to our Astra database. The Stargate API framework allows developers the freedom to access Astra with a variety of APIs, the Document API being one. With the Document API, you can save and search schemaless JSON documents in Cassandra. No need to use SQL, CQL, or any database drivers to talk to the data layer. Just code and move on.
ehem...for those of you familiar with Apache Cassandra, yes, I just said you could skip data modeling.
For a FULL code solution to this section right-click
the image below and choose Open Link in New Tab
.
Don't forget to SAVE your changes or enable autosave in GitPod.
ASTRA
service is available at url https://astra.datastax.com. ASTRA
is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. Astra
offers 5 Gb Tier Free Forever and you don't need a credit card or anything to sign-up and use it.
✅ Step 1a. Register (if needed) and Sign In to Astra : |
---|
🖱️ Click me to show detailsYou can use your Github , Google accounts or register with an email .Make sure to chose a password with minimum 8 characters, containing upper and lowercase letters, at least one number and special character - Registration Page - Authentication Page |
✅ Step 1b. Choose the free plan and select your region |
---|
✅ Step 2a: Set environment variables in code
In the "hello world" section, we pushed a simple helloWorld "test" function to get a feel for how things work. Now, we are going to start working with the "real" code to get our game working. To do this, we need to set a group of environment variables referring to the database we just created with DataStax Astra, not only in our code, but across GitHub and Netlify as well.
Why are we doing this, you might ask? Because as part of our CI/CD pipeline our tests will attempt to connect to our data layer to ensure everything is hooked up and working. Not only that, but once you deploy your application to Netlify it will use these variables to hook up your production app and power your serverless functions.
We set these all ONE time and that's it, you are ready to go. With that, let's do it.
First things first, we need to get the values we are going to use for our variables. This section might look like a lot, but honestly, it's just a bunch of copy/paste.
✔ Go back to the Astra UI and click on the database you just created to get the details page.
🟢 ASTRA_DB_USERNAME
as battle_user
(The user name we defined when creating the Astra instance)
🟢 ASTRA_DB_PASSWORD
as battle_password1
(The password we defined when creating the Astra instance)
🟢 ASTRA_DB_KEYSPACE
as battlestax
(The keyspace we defined when creating the Astra instance)
🟢 ASTRA_DB_ID
as the cluster ID of your Astra DB. To get your database ID and region go the ASTRA summary page. Locate the cluster ID and copy it by clicking the clickboard icon as shown above.
🟢 ASTRA_DB_REGION
as the region you picked when creating the DB, It should be either us-east1
or europe-west1
.
🟢 GAMES_COLLECTION
as games
(this is the collection where we will store all values)
Now, take these values and apply them to the following sections. You will use each one more than once FYI.
The following instructions are the same whether using GitPod or a local IDE.
✔ Ensure that you are in the ??battlestax??? directory
✔ Copy and paste the contents of the .env.template
file into an .env
file:
📘 Command to execute
cat .env.example > .env
✔ The .env
file allows us to customize our own environmental variables. We set our Astra credentials to env variables, which are outside of our program. Fill in the .env
file variables with the Astra variables you made a copy of above:
If you used different values for your database you will need to use those instead, otherwise just use the values we've provided.
ASTRA_DB_USERNAME=battle_user
ASTRA_DB_PASSWORD=battle_password1
ASTRA_DB_KEYSPACE=battlestax
ASTRA_DB_ID=[the value you retrieved above from YOUR database]
ASTRA_DB_REGION=[the value you retrieved above from YOUR database]
GAMES_COLLECTION=games
✅ Step 2b: Set environment variables (secrets) in GitHub for CI/CD
Every application should have a CI/CD (Continuous Integration, Continuous Deployment) pipeline. This allows for quick iteration of changes to production deployment by taking advantage of automation and tests to ensure everything is working properly.
After each commit a workflow is initialized to BUILD your project, EXECUTE tests and DEPLOY to Netlify. The good thing is many CI/CD tools are provided right within GitHub. Adding this capability just takes a couple steps.
✔ Within YOUR Battlestax repository in GitHub click on Settings
in the top toolbar, choose Secrets
from the menu on the left, and finally click the New secret
button on the top right of the page. Add a secret for each of the variables we used earlier.
ASTRA_DB_USERNAME=battle_user
ASTRA_DB_PASSWORD=battle_password1
ASTRA_DB_KEYSPACE=battlestax
ASTRA_DB_ID=[the value you retrieved above from YOUR database]
ASTRA_DB_REGION=[the value you retrieved above from YOUR database]
GAMES_COLLECTION=games
This should look like:
✅ Step 2c: Set environment variables in Netlify
✔ Go back to Netlify and navigate to Site settings
in the toolbar, then choose Build & deploy
from the menu on the left.
✔ Scroll down to the Environment variables
section and choose Edit variables
.
✔ Click New variable
to add each key/value pair, one for each variable listed below.
ASTRA_DB_USERNAME=battle_user
ASTRA_DB_PASSWORD=battle_password1
ASTRA_DB_KEYSPACE=battlestax
ASTRA_DB_ID=[the value you retrieved above from YOUR database]
ASTRA_DB_REGION=[the value you retrieved above from YOUR database]
GAMES_COLLECTION=games
You should now have something like:
✔ Once complete click Save
and you are good to go.
You don't have to do that ever again, we promise. From now on anytime you deploy everything should be hooked up to your data layer and ready to go through to production.
Now that we have ALLLLL of our environment vars set and our game document store, let's start building our basic insertGame
serverless function, in the function/insertGame.js
file.
First, we need to declare gameId
and gamePayload
variables. We know each game is associated with it's own unique game id, and during game play we can anticipate getting a payload from the user.
📘 Code to copy
// let's return a 400 if we don't recieve a valid game id
let gameId;
let gamePayload;
From Netlify, you get your gameId
parameter from the path of the incoming REST call, and parse our event body that is associated with it as the gamePayload
.
By default, Netlify puts your function at the path /.netlify/function/insertGame
.
📘 Code to copy
// let's set the game id
gameId = event.path.split("insertGame/")[1];
// let's parse the incoming payload
gamePayload = JSON.parse(event.body);
Let's not forget about error handling. If we cannot parse the game id or body from the incoming REST call, a HTTP status code of 400
will be returned and you will get an error message
📘 Code to copy
...
} catch (e) {
// let's return a 400 if we can't parse the game id or body
return {
statusCode: 400,
body: JSON.stringify({ message: "must provide a valid game ID" }),
};
}
All this should statisfy our second test ( we need to get valid game id)
The next thing we need to do is to connect to our Astra database. We are first going to import the JavaScript SDK library (astrajs
) to create our Astra Client. We are going to give the Astra client our environment variable credentials to connect to the database, and create a document to store information about our game.
Go HERE for more information on the on the JavaScript SDK library.
📘 Code to copy
// let's connect to Astra
const astraClient = await createClient({
// let's set our Astra connection configuration
astraDatabaseId: process.env.ASTRA_DB_ID,
astraDatabaseRegion: process.env.ASTRA_DB_REGION,
username: process.env.ASTRA_DB_USERNAME,
password: process.env.ASTRA_DB_PASSWORD,
});
const gamesCollection = astraClient
.namespace(process.env.ASTRA_DB_KEYSPACE)
.collection(process.env.GAMES_COLLECTION);
Finally, we are going to try to take all our configuration infomation stored in gamesCollection
and provision a new game from it. If it works, we get back a HTTP status code of 200
. If it fails, we will get back a 500
. This should statify both test 1 and test 2.
📘 Code to copy
// let's provision a new game
try {
// let's create a new game with the gamesCollection
const res = await gamesCollection.create(gameId, gamePayload);
// let's return a 200 with the resoponse from astra
return {
statusCode: 200,
body: JSON.stringify(res),
};
} catch (e) {
console.error(e);
// let's return a 500 on error
return {
statusCode: 500,
body: JSON.stringify(e),
};
}
};
For a FULL code solution to this section right-click
the image below and choose Open Link in New Tab
.
The way we approach testing is by asking the question "What does our endpoint need to do?". We want our serverlesss function to provision a new game on Astra -- and we need provide the API with a random game code so this can work. Our endpoint needs to:
- Tell the API to make the game document
- It should not be able to make a game document if we don't give it a valid game id
- If we get a 500 on error (something goes wrong), we should be informed
We are provided with test cases test/insertGame.test.js
that will check for these actions working in functions/insertGame.js
. Notice that insertGame
is required as a dependency in our test.js file.
We are going to use faker.js
, a JavaScript library for generating mock data. This mock data is useful when building and testing our application. Hence, we should require
the faker library.
const faker = require("faker");
✔️ TEST 1: Our API should make the game document. We need to test to see if the insertGame
function actually does that:
const gameId = faker.helpers.replaceSymbols("????");
it("should create a game document", async () => {
const response = await insertGame.handler({
path: "/functions/insertGame/" + gameId,
body: '{"user":"me"}',
});
expect(response.statusCode).toBe(200);
});
We use a simple async function to do this. faker.helpers.replaceSymbols("????")
will create a sample game id for the path, and some user data in the
body. As successful test run will return a 200
.
✔️ TEST 2 : Our function must not be able to create a game document with a valid game id
it("shouldn't create a game document without a game id", async () => {
const response = await insertGame.handler({ path: "insertGame" });
expect(response.statusCode).toBe(400);
});
We ensure that our function cannot go ahead and create a game, unless it has been provided with a valid game id.
Now let's run our tests to see if our function works.
📘 Command to execute
npm run test:functions
📗 Expected output
Great job! Let's move to the next section.
🏠 Back to Table of Contents or move to the next section => 📚 What is Redux and React