Quick Start with CLI
π₯π€β¨οΈππ
This guide will introduce some basic code.store concepts like services and schema generation using CLI. The goal of this quick start is to get you up and running fast, so let's not waste any time and start!
Create code.store account
If you haven't done this already, then click on this link.
Install the code.store CLI
We invite you to follow this guide and install our command-line interface:
Getting startedAuthenticate and create service
Once you have your account and the CLI is installed, we can finally do some interesting stuff!
In order to work with the CLI, you have to connect it with your account by launching the following command:
codestore login
This command is going to launch your default browser and ask for your login details. Once you are authenticated, we can go on and create a new service.
codestore service:create
The anatomy of a service
You can check the contents of your new service directory by running ls -lh ./
# Example of the directory structure of a Service
./
βββ codestore.yaml # main configuration file
βββ package.json # standard NPM configuration file
βββ src
βββ data # TypeORM entities
β βββ entities
β βββ migrations
βββ resolvers # GraphQL resolvers
β βββ mutations # mutations as used to create new objects
β βββ queries # queries are used to retrieve objects
β β βββ helloWorld.ts
β βββ resolvers.ts
βββ schema.graphql # GraphQL definition of your service's API
Let's get into the details of each file and directory.
The root directory contains two files and one folder:
package.json
β it is a standard NPM configuration file. Feel free and add any NPM packages you like usingnpm install {packageName}
;codestore.yaml
β contains your service ID and will contain more configuration options in later versions;src/
β this is where all the source code lives.
Let's dive into the src/
directory:
schema.graphql
β one of the most (if not the most) important files. It contains a GraphQL schema of your service;data/
β this directory contains TypeORM entities. You can automatically generate TypeScript classes for your database tables usingcs generate:models -p src/data
command;resolvers/
β this is where your business logic lives. Resolvers serve two purposes: connect your GraphQL objects to data in the database and is a place where you implement any additional business logic.
Basic API schema and resolver
When you create a new service, we are generating it with default schema.graphql
which contains the following code:
type Query {
helloWorld: String!
}
It is a very basic schema of an API that has a single Query
called helloWorld
(which doesn't accept arguments) and which returns a single output of type string
.
We can test this query by running the following curl command in your terminal:
curl \
-X POST \
-H "Content-Type: application/json" \
--data '{ "query": "{ helloWorld }" }' \
https://api.code.store/{service_url_hash}/graphql
Hopefully, we should get "Hello, World!" response (something looking like that {"data":{"helloWorld":"Hello, World!"}}
) in our terminal π€
Let's take a look at the resolver (business logic) for this query which is located in the file src/resolvers/queries/helloWorld.ts
:
// src/resolvers/queries/helloWorld.ts
import { logger, Resolver } from 'codestore-utils';
const resolver: Resolver = async (parent, args, context, info) => {
logger.log('This is a helloWorld resolver!', 'helloWorld');
return 'Hello, World!';
}
export default resolver;
This is a basic code which does few things:
we are importing a logger module which is going to output messages to the local console or to our cloud log storage;
we are importing a Resolver, which is an Interface for a function type; it is not a necessary step but it is a useful one if you want to see type hints in your IDE;
if your code editor cannot find 'codestore-utils' then simply run
npm install
in the service directory root.
This should give you a basic understanding of how GraphQL schema and resolvers work together and let's write something more meaningful in the next chapter.
Local development
The local development workflow is still in early beta and we are working hard to bring you the most stable and comfortable local development experience. If you notice any issues, please ping us in our community.
Local development comes in handy if you don't want to launch codestore push
after each modification and then wait until it finishes.
In order to use local development workflow:
install PostgreSQL database locally; you can do it quite easily either https://postgresapp.com/ or via Docker;
configure your local settings in codestore.yaml:
# ./codestore.yaml
localConfiguration:
database:
port: 5432
database: my-local-database
password: my-secure-password
username: my-db-username
host: localhost
application:
port: 3000
launch a local development server by running
codestore dev
codestore dev
will try to run "npm install" to make sure that the dependencies of your service are installed but you can also launch it manually if you wish.
After launching codestore dev
you should see the following output:
$ codestore dev
β Installing dependencies
β Compiling code
2020-08-10T16:12:18.250Z Starting development server
2020-08-10T16:12:18.251Z [bootstrap] Start bootstrapping the application
2020-08-10T16:12:18.251Z [DatabaseConnector] Connecting to database
2020-08-10T16:12:18.279Z [DatabaseConnector] Successfully connected to database my-local-database
2020-08-10T16:12:18.279Z [DatabaseConnector] Running migrations
2020-08-10T16:12:18.289Z [DatabaseConnector] Loaded entities:
2020-08-10T16:12:18.735Z [GraphqlLoader] Loaded queries: helloWorld
2020-08-10T16:12:18.749Z [bootstrap] Server is running on http://localhost:3000
2020-08-10T16:12:18.749Z [bootstrap] Graphql is available on http://localhost:3000/graphql
Voila ! Let's try and test our local server:
curl \
-X POST \
-H "Content-Type: application/json" \
--data '{ "query": "{ helloWorld }" }' \
http://localhost:3000/graphql
You should get a "Hello, World!" message if everything works properly π€
Blog-post example
We can finally write something more or less meaningful! For the sake of simplicity, we decided to implement a well-beaten example of a Blog-post. We are going to be using the local development server during this example, so make sure that you have a PostgreSQL database ready.
First of all, we should modify our schema.graphql
file:
# src/schema.graphql
# API schema for a simple Blog-post service
type Post {
id: ID!
createdAt: String!
title: String!
body: String!
authorName: String!
}
type Query {
allPosts: [Post]
}
We have added two types and two queries in the schema and we can now go on and add the resolvers. Let's begin with the query allPosts
: create a file src/resolvers/queries/allPosts.ts
and initialize it with the following code:
// src/resolvers/queries/allPosts.ts
import { logger, Resolver } from 'codestore-utils';
const resolver: Resolver = async (parent, args, context, info) => {
logger.log('This is a allPosts resolver!', 'allPosts');
return [
{
id: 1,
createdAt: '2020-06-30',
title: 'Test post',
body: 'Test body',
authorName: 'Test author',
}
];
}
export default resolver;
Each GraphQL query or mutation must be mapped to a resolver. In order to find a resolver for a query/mutation, code.store is using a file structure mapping to find a corresponding resolver.
The rules are simple:
all queries should be placed into
src/resolvers/queries/{queryName}.ts
files,all mutations into
src/resolvers/mutations/{mutationName}.ts
For example, resolver for a mutation createUser
should be placed into src/resolvers/mutations/createUser.ts
file.
Here we go, this is our first test resolver which is not yet connected to anything but which already can return the data! One last step before running the deployment command - we have to remove src/resolvers/queries/helloWorld.ts
or simply rename it to helloWorld.ts_
. And that's it, you can now run your application locally via codestore dev
and test it via CURL:
curl \
-X POST \
-H "Content-Type: application/json" \
--data '{ "query": "{ allPosts { id title authorName } }" }' \
http://localhost:3000/graphql
GraphQL queries and mutations in your schema.graphql
should match 1-to-1 the queries and migrations in the file system, i.e. if you have a query "test" in a schema and you don't have in the file system, the service will throw an error. The same in the opposite order, if you have a resolver in the file system but not in the schema.
Until now we were not using any database at all and the time has come to grab a beer create one. The cool thing is that code.store can generate the database automatically based on the GraphQL schema you provided! In order to generate the entities locally, we should run codestore generate:models -p src/data
command which will generate the entities based on your schema. As soon as it finishes the generation, let's modify our resolver and add some database queries:
// src/resolvers/queries/allPosts.ts
import { logger, Resolver } from 'codestore-utils';
import Post from '../../data/entities/Post';
const resolver: Resolver = async (parent, args, context, info) => {
logger.log('This is a allPosts resolver!', 'allPosts');
const postRepository = context.db.connection.getRepository(Post);
return postRepository.find();
}
export default resolver;
Few things have changed here:
we are importing TypeORM Repository and our generated Post entity
we are initializing the repository and performing a find query for our Post entity
As soon as we run the application with codestore dev
, we can test it again and see that it's returning an empty array as we haven't created anything yet π€¦π½ββοΈ.
curl \
-X POST \
-H "Content-Type: application/json" \
--data '{ "query": "{ allPosts { id title authorName } }" }' \
http://localhost:3000/graphql
Time to add the first mutation.
First of all, modify your schema.graphql to look like the following:
# src/schema.graphql
# API schema for a simple Blog-post service
type Post {
id: ID!
createdAt: String!
title: String!
body: String!
authorName: String!
}
type Query {
allPosts: [Post]
}
type Mutation {
createPost(title: String!, body: String!, authorName: String!): Post
}
This is what has changed, we added a type Mutation to our GraphQL schema containing a mutation createPost
. Now we can implement our resolver:
// src/resolvers/mutations/createPost.ts
import { logger, Resolver } from 'codestore-utils';
import Post from '../../data/entities/Post';
const resolver: Resolver = async (parent, args, context, info) => {
logger.log('creating a new Post', 'createPost');
const post = new Post();
post.title = args.title;
post.authorName = args.authorName;
post.body = args.body;
post.createdAt = new Date().toISOString();
logger.log(post, 'createPost');
// Getting a database connection
const repository = context.db.connection.getRepository(Post);
// Saving our first post entity
return await repository.save(post);
}
export default resolver;
Now launch codestore dev
again and run some queries (we are going to be using a http command which is similar to cURL but provides a better output):
# Create a couple of new posts
$ curl 'http://localhost:3000/graphql' \
-X POST \
-H 'Content-Type: application/json' \
--data '{"query":"mutation createPost($title:String!, $body:String!, $authorName:String!) { createPost(title:$title, body:$body, authorName:$authorName) { id title body authorName } }","variables":{"title":"Our First Article","body":"Body","authorName":"Arthur Conan Doyle"}}'
# And you will get output like this, meaning post successfully created
{"data":{"createPost":{"id":"1","title":"Our First Article","body":"Body","authorName":"Arthur Conan Doyle"}}}
# Create another one
$ curl 'http://localhost:3000/graphql' \
-X POST \
-H 'Content-Type: application/json' \
--data '{"query":"mutation createPost($title:String!, $body:String!, $authorName:String!) { createPost(title:$title, body:$body, authorName:$authorName) { id title body authorName } }","variables":{"title":"Our Second Article","body":"Body","authorName":"Arthur Conan Doyle"}}'
# Output
{"data":{"createPost":{"id":"2","title":"Our Second Article","body":"Body","authorName":"Arthur Conan Doyle"}}}
# Let's retrieve the posts now
$ curl \
-X POST \
-H "Content-Type: application/json" \
--data '{ "query": "{ allPosts { id title authorName } }" }' \
http://localhost:3000/graphql
# All posts that we created for now
{"data":{"allPosts":[{"id":"1","title":"Our First Article","authorName":"Arthur Conan Doyle"},{"id":"2","title":"Our Second Article","authorName":"Arthur Conan Doyle"}]}}
# Or if we format the JSON for more readability
{
"data": {
"allPosts": [
{
"id": "1",
"title": "Our First Article",
"authorName": "Arthur Conan Doyle"
},
{
"id": "2",
"title": "Our Second Article",
"authorName": "Arthur Conan Doyle"
}
]
}
}
Finalizing
So now we have a working service on our local machine and it would be great to have it deployed somewhere in the cloud. Let's do exactly that and deploy the service to the private environment by running codestore push
command. As soon as finished, you can launch the queries in your private environment!
Conclusion
Not sure if it was quick π but we hope that after this guide you have a good understanding of how code.store works. Feel free to check the rest of the documentation and contact us in the community chat if you will have any questions!
Happy coding and may the force be with you! π¦Ύ
Last updated
Was this helpful?