Stargate GraphQL CQL-first API QuickStart
Time to complete: 5 minutes
Stargate is a data gateway deployed between client applications and a database. In this QuickStart, you’ll be up and running on your local machine with the GraphQL API plugin that exposes CRUD access to data stored in Cassandra tables.
For more information about the GraphQL API, see the blog post on the GraphQL API. |
Prerequisites
If you are looking to just get started, DataStax Astra Database-as-a-Service can get you started with no install steps. |
-
Install cURL, a utility for running REST, Document, or GraphQL queries on the command line.
-
[Optional] If you prefer, you can use Postman as a client interface for exploring the APIs
-
You will also find links to downloadable collections and environments in Using Postman
-
-
[Optional] If you going to use the GraphQL API, you will want to use the GraphQL Playground to deploy schema and execute mutations and queries.
-
[Optional] For the REST and Document APIs, you can use the Swagger UI.
-
Install Docker for Desktop
-
Pull a Stargate Docker image
v2
For Stargate v2, you’ll need to pull an image for coordinator, plus an image for each API that you wish to run: restapi, graphql, and docsapi. The coordinator image contains a Apache Cassandra™ backend, the Cassandra Query Language (CQL), and the gRPC API.
The following are the commands for each of those images using the tag v2
:
docker pull stargateio/coordinator-4_0:v2
docker pull stargateio/restapi:v2
docker pull stargateio/docsapi:v2
docker pull stargateio/graphqlapi:v2
v1
This image contains the Cassandra Query Language (CQL), REST, Document, GraphQL APIs, and GraphQL Playground, along with an Apache Cassandra™ 4.0 backend.
docker pull stargateio/stargate-4_0:v1.0.57
v2
For Stargate v2, you’ll need to pull an image for coordinator, plus an image for each API that you wish to run: restapi, graphql, and docsapi. The coordinator image contains a Apache Cassandra™ backend, the Cassandra Query Language (CQL), and the gRPC API.
The following are the commands for each of those images using the tag v2
:
docker pull stargateio/coordinator-3_11:v2
docker pull stargateio/restapi:v2
docker pull stargateio/docsapi:v2
docker pull stargateio/graphqlapi:v2
v1
This image contains the Cassandra Query Language (CQL), REST, Document, GraphQL APIs, and GraphQL Playground, along with an Apache Cassandra™ 3.11 backend.
docker pull stargateio/stargate-3_11:v1.0.57
v2
For Stargate v2, you’ll need to pull an image for coordinator, plus an image for each API that you wish to run: restapi, graphql, and docsapi. The coordinator image contains a Apache Cassandra™ backend, the Cassandra Query Language (CQL), and the gRPC API.
The following are the commands for each of those images using the tag v2
:
docker pull stargateio/coordinator-68:v2
docker pull stargateio/restapi:v2
docker pull stargateio/docsapi:v2
docker pull stargateio/graphqlapi:v2
v1
This image contains the Cassandra Query Language (CQL), REST, Document, GraphQL APIs, and GraphQL Playground, along with a DataStax Enterprise™ 6.8 backend.
docker pull stargateio/stargate-dse-68:v1.0.57
-
Run the Stargate Docker image
v2
Use this docker-compose shell script to start the coordinator and APIs in developer mode.
The easiest way to do that is to navigate to the <install_location>/stargate/docker-compose
directory, and run the script.
You will want to run, for example:
./start_cass_4_0_dev_mode.sh
This command will start using the latest available coordinator and API images with the v2
tag.
You may also select a specific image tag using the -t <image_tag>
option. A list of the available tags for the coordinator can be found here.
v1
Start the Stargate container in developer mode. Developer mode removes the need to set up a separate Cassandra instance and is meant for development and testing only.
docker run --name stargate \
-p 8080:8080 \
-p 8081:8081 \
-p 8082:8082 \
-p 127.0.0.1:9042:9042 \
-d \
-e CLUSTER_NAME=stargate \
-e CLUSTER_VERSION=4.0 \
-e DEVELOPER_MODE=true \
stargateio/stargate-4_0:v1.0.57
v2
Use this docker-compose shell script to start the coordinator and APIs in developer mode.
The easiest way to do that is to navigate to the <install_location>/stargate/docker-compose
directory, and run the script.
You will want to run, for example:
./start_cass_3_11_dev_mode.sh
This command will start using the latest available coordinator and API images with the v2
tag.
You may also select a specific image tag using the -t <image_tag>
option. A list of the available tags for the coordinator can be found here.
v1
Start the Stargate container in developer mode. Developer mode removes the need to set up a separate Cassandra instance and is meant for development and testing only.
docker run --name stargate \
-p 8080:8080 \
-p 8081:8081 \
-p 8082:8082 \
-p 127.0.0.1:9042:9042 \
-d \
-e CLUSTER_NAME=stargate \
-e CLUSTER_VERSION=3.11 \
-e DEVELOPER_MODE=true \
stargateio/stargate-3_11:v1.0.57
v2
Use this docker-compose shell script to start the coordinator and APIs in developer mode.
The easiest way to do that is to navigate to the <install_location>/stargate/docker-compose
directory, and run the script.
You will want to run, for example:
./start_dse_68_dev_mode.sh
This command will start using the latest available coordinator and API images with the v2
tag.
You may also select a specific image tag using the -t <image_tag>
option. A list of the available tags for the coordinator can be found here.
v1
Start the Stargate container in developer mode. Developer mode removes the need to set up a separate DSE instance and is meant for development and testing only.
docker run --name stargate \
-p 8080:8080 \
-p 8081:8081 \
-p 8082:8082 \
-p 127.0.0.1:9042:9042 \
-d \
-e CLUSTER_NAME=stargate \
-e CLUSTER_VERSION=6.8 \
-e DEVELOPER_MODE=true \
stargateio/stargate-dse-68:v1.0.57
-
Generate an authorization token to access the interface by following the instructions in Table-based authentication/Authorization
You will need to add this token to the GraphQL Playground in order to authorize
your GraphQL requests. Copy the value after "authToken"
to use later.
Using the GraphQL Playground
The easiest way to get started with GraphQL is to use the built-in GraphQL playground.
In Stargate, go to your browser and launch the url:
http://localhost:8080/playground
Add your application token to the HTTP HEADERS section at the lower lefthand corner of the GraphQL Playground window:
{"x-cassandra-token":"$AUTH_TOKEN"}
Once in the playground, you can create new schema and interact with the GraphQL APIs. The server paths are structured to provide access to creating and querying schema, as well as querying and modifying Cassandra data:
-
/graphql-schema
-
An API for exploring and creating schema, or Data Definition Language (DDL). For example, Stargate has queries to create, modify, or drop tables, such as
createTable
, ordropTable
.
-
-
/graphql/<keyspace>
-
An API for querying and modifying your tables using GraphQL fields. Generally, you will start the playground with
/graphql-schema
to create some schema.
-
Creating schema
In order to use the GraphQL API, you must create schema that defines the keyspace and
tables that will store the data. A keyspace is a container for which a
replication factor
defines the number of data replicas the database will store.
Tables consist of columns that have a defined data type. Multiple tables are contained
in a keyspace, but a table cannot be contained in multiple keyspaces.
Create a keyspace
Before you can start using the GraphQL API, you must first create a Cassandra keyspace and at least one table in your database. If you are connecting to a Cassandra database with existing schema, you can skip this step.
Inside the GraphQL playground, navigate to http://localhost:8080/graphql-schema and create a keyspace by executing the following mutation:
# create a keyspace called library
mutation createKsLibrary {
createKeyspace(name:"library", replicas: 1)
}
For each keyspace created in your Cassandra schema, a new path is created under
the graphql-path
root (default is: /graphql
). For example, the mutation just
executed creates a path /graphql/library
for the library
keyspace when
Cassandra creates the keyspace.
Add the auth token to the HTTP Headers box in the lower lefthand corner:
{
"X-Cassandra-Token":"bff43799-4682-4375-99e8-23c8a9d0f304"
}
Notice that the key for this JSON token is different than the value that the
generate token has. It is |
Now run the mutation to create the keyspace. You should see a return value of:
{
"data": {
"createKeyspace": true
}
}
Check keyspace existence
To check if a keyspace exists, execute a GraphQL query:
# Works in graphql-schema
# for either CQL-first or schema-first
query GetKeyspace {
keyspace(name: "library") {
name
dcs {
name
replicas
}
tables {
name
columns {
name
kind
type {
basic
info {
name
}
}
}
}
}
}
{
"data": {
"keyspace": {
"name": "library",
"dcs": [],
"tables": [
{
"name": "book",
"columns": [
{
"name": "title",
"kind": "PARTITION",
"type": {
"basic": "VARCHAR",
"info": null
}
},
{
"name": "author",
"kind": "CLUSTERING",
"type": {
"basic": "VARCHAR",
"info": null
}
},
{
"name": "format",
"kind": "REGULAR",
"type": {
"basic": "SET",
"info": {
"name": null
}
}
},
{
"name": "genre",
"kind": "REGULAR",
"type": {
"basic": "SET",
"info": {
"name": null
}
}
},
{
"name": "isbn",
"kind": "REGULAR",
"type": {
"basic": "VARCHAR",
"info": null
}
},
{
"name": "language",
"kind": "REGULAR",
"type": {
"basic": "VARCHAR",
"info": null
}
},
{
"name": "pub_year",
"kind": "REGULAR",
"type": {
"basic": "INT",
"info": null
}
}
]
},
{
"name": "reader",
"columns": [
{
"name": "name",
"kind": "PARTITION",
"type": {
"basic": "VARCHAR",
"info": null
}
},
{
"name": "user_id",
"kind": "CLUSTERING",
"type": {
"basic": "UUID",
"info": null
}
},
{
"name": "addresses",
"kind": "REGULAR",
"type": {
"basic": "LIST",
"info": {
"name": null
}
}
},
{
"name": "birthdate",
"kind": "REGULAR",
"type": {
"basic": "DATE",
"info": null
}
},
{
"name": "email",
"kind": "REGULAR",
"type": {
"basic": "SET",
"info": {
"name": null
}
}
},
{
"name": "reviews",
"kind": "REGULAR",
"type": {
"basic": "TUPLE",
"info": {
"name": null
}
}
}
]
}
}
}
}
Create a table
After the keyspace exists, you can create a table by executing mutations. For this example, two tables are created:
# create two tables (book, reader) in library with a single mutation
# DATA TYPES: TEXT, UUID, SET(TEXT), TUPLE(TEXT, INT, DATE), LIST(UDT)
mutation createTables {
book: createTable(
keyspaceName:"library",
tableName:"book",
partitionKeys: [ # The keys required to access your data
{ name: "title", type: {basic: TEXT} }
]
clusteringKeys: [
{ name: "author", type: {basic: TEXT} }
]
)
reader: createTable(
keyspaceName:"library",
tableName:"reader",
partitionKeys: [
{ name: "name", type: {basic: TEXT} }
]
clusteringKeys: [ # Secondary key used to access values within the partition
{ name: "user_id", type: {basic: UUID}, order: "ASC" }
]
values: [
{ name: "birthdate", type: {basic: DATE} }
{ name: "email", type: {basic: SET, info:{ subTypes: [ { basic: TEXT } ] } } }
{ name: "reviews", type: {basic: TUPLE, info: { subTypes: [ { basic: TEXT }, { basic: INT }, { basic: DATE } ] } } }
{ name: "addresses", type: { basic: LIST, info: { subTypes: [ { basic: UDT, info: { name: "address_type", frozen: true } } ] } } }
]
)
}
"data": {
"book": true,
"reader": true
}
}
It is worth noting that one mutation is used to create two tables. Information about partition keys and clustering keys can be found in the CQL reference.
The second table, reader
, also defines a column using a
user-defined type (UDT).
One of these tables includes creating a column with the data type LIST
, an
ordered collection of text values.
Collection (set, list, map) columns
Including a collection in a table has a couple of extra parts:
# create a table with a MAP
# DATA TYPE: TEXT, INT, MAP(TEXT, DATE)
# Sample: btype=Editor, badge_id=1, earned = [Gold:010120, Silver:020221]
mutation createMapTable {
badge: createTable (
keyspaceName:"library",
tableName: "badge",
partitionKeys: [
{name: "btype", type: {basic:TEXT}}
]
clusteringKeys: [
{ name: "badge_id", type: { basic: INT} }
],
ifNotExists:true,
values: [
{name: "earned", type:{basic:LIST { basic:MAP, info:{ subTypes: [ { basic: TEXT }, {basic: DATE}]}}}}
]
)
}
{
"data": {
"badge": true
}
}
This example shows a map. A previous example shows a list. In the next example, a set will be defined.
Add columns to table schema
If you need to add more attributes to something you are storing in a table, you can add one or more columns:
# alter a table and add columns
# DATA TYPES: TEXT, INT, SET(TEXT)
mutation alterTableAddCols {
alterTableAdd(
keyspaceName:"library",
tableName:"book",
toAdd:[
{ name: "isbn", type: { basic: TEXT } }
{ name: "language", type: {basic: TEXT} }
{ name: "pub_year", type: {basic: INT} }
{ name: "genre", type: {basic:SET, info:{ subTypes: [ { basic: TEXT } ] } } }
{ name: "format", type: {basic:SET, info:{ subTypes: [ { basic: TEXT } ] } } }
]
)
}
{
"data": {
"alterTableAdd": true
}
}
Check table and column existence
To check if a table or particular table columns exist, execute a GraphQL query:
query GetTables {
keyspace(name: "library") {
name
tables {
name
columns {
name
kind
type {
basic
info {
name
}
}
}
}
}
}
{
"data": {
"keyspace": {
"name": "library",
"tables": [
{
"name": "reader",
"columns": [
{
"name": "name",
"kind": "PARTITION",
"type": {
"basic": "VARCHAR",
"info": null
}
},
]
},
{
"name": "book",
"columns": [
{
"name": "title",
"kind": "PARTITION",
"type": {
"basic": "VARCHAR",
"info": null
}
},
{
"name": "author",
"kind": "REGULAR",
"type": {
"basic": "VARCHAR",
"info": null
}
},
{
"name": "isbn",
"kind": "REGULAR",
"type": {
"basic": "VARCHAR",
"info": null
}
}
]
}
]
}
}
}
Because these queries are named, the GraphQL playground will allow you to select
which query to run. The first query will return information about the keyspace
library
and the tables within it. The second query will return just information
about the tables in that keyspace.
Interacting with tables
API generation
Once schema is created, the GraphQL API generates mutations and queries can be used. In the GraphQL playground, expand the tabs on the righthand side labelled "DOCS" or "SCHEMA", to discover the items available and the syntax to use.
For each table in the Cassandra schema that we just created, several GraphQL
fields are created for
handling queries and mutations. For example, the GraphQL API generated for the
books
table is:
schema {
query: Query
mutation: Mutation
}
type Query {
book(value: bookInput, filter: bookFilterInput, orderBy: [bookOrder], options: QueryOptions): bookResult
bookFilter(filter: bookFilterInput!, orderBy: [bookOrder], options: QueryOptions): bookResult
}
type Mutation {
insertbook(value: bookInput!, ifNotExists: Boolean, options: UpdateOptions): bookMutationResult
updatebook(value: bookInput!, ifExists: Boolean, ifCondition: bookFilterInput, options: UpdateOptions): bookMutationResult
deletebook(value: bookInput!, ifExists: Boolean, ifCondition: bookFilterInput, options: UpdateOptions): bookMutationResult
}
The query books()
can query book values by equality. If no value argument is
provided, then the first hundred (default pagesize) values are returned.
Several mutations are created that you can use to insert, update, or delete books. Some important facts about these mutations are:
-
insertBooks()
is an upsert operation if a book with the same information exist, unless theifNotExists
is set to true. -
updateBooks()
is also an upsert operation, and will create a new book if it doesn’t exist, unlessifNotExists
is set to true. -
Using the
ifNotExists
orifCondition
options affects the performance of operations because of the compare-and-set execution path in Cassandra. Under the hood these operations are using a feature in Cassandra called lightweight transactions (LWTs).
As more tables are added to a keyspace, additional GraphQL fields will add query and mutation types that can be used to interact with the table data.
Insert data
Any of the created APIs can be used to interact with the GraphQL data, to write or read data.
First, let’s navigate to your new keyspace library
inside the playground.
Change the location to
http://localhost:8080/graphql/library
and add a couple of books to the book
table:
# insert 2 books in one mutation
mutation insert2Books {
moby: insertbook(value: {title:"Moby Dick", author:"Herman Melville"}) {
value {
title
}
}
catch22: insertbook(value: {title:"Catch-22", author:"Joseph Heller"}) {
value {
title
}
}
}
{
"data": {
"moby": {
"value": {
"title": "Moby Dick"
}
},
"catch22": {
"value": {
"title": "Catch-22"
}
}
}
}
Note that the keyword value
is used twice in the mutation.
The first use defines the value that the record is set to, for instance, the title
to Moby Dick and the author to Herman Melville.
The second use defines the values that will be displayed after the success
of the mutation, so that proper insertion can be verified.
This same method is valid for updates and read queries.
Retrieve data
Let’s check that the data was inserted.
Now let’s search for a particular record using a WHERE
clause. The primary
key of the table can be used in the WHERE
clause, but non-primary key columns
cannot be used unless indexed.
The following query, looking at the location
http://localhost:8080/graphql/library
will get both the title
and the author
for the specified book WHERE
title:"Moby Dick"
:
Update data
Using the column that we added earlier, the data for a book is updated with the
ISBN
value:
mutation updateOneBook {
moby: updatebook(value: {title:"Moby Dick", author:"Herman Melville", isbn: "9780140861723"}, ifExists: true ) {
value {
title
author
isbn
}
}
}
{
"data": {
"moby": {
"value": {
"title": "Moby Dick",
"author": "Herman Melville",
"isbn": "9780140861723"
}
}
}
}
Updates are upserts. If the row doesn’t exist, it will be created. If it does exist, it will be updated with the new row data. |
Delete data
After adding the book "Pride and Prejudice" with an insertBooks()
, you can delete
the book using deleteBooks()
to illustrate deleting data:
mutation deleteOneBook {
PaP: deletebook(value: {title:"Pride and Prejudice", author: "Jane Austen"}, ifExists: true ) {
value {
title
}
}
}
{
"data": {
"PaP": {
"value": {
"title": "Pride and Prejudice"
}
}
}
}
Note the use of ifExists
to validate that the book exists before deleting it.
For more information on the GraphQL API (CQL-first), see Using the GraphQL API (CQL-first) in the Developing with Stargate APIs section.