Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add new quickstart for customer registry in javascript #379

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,26 @@ jobs:
npm install ../sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz
npm test
npm pack
- run:
name: "bundle quickstarts"
working_directory: "~/project/docs"
command: |
make bundles
- run:
name: "Test quickstart: JS Customer Registry"
working_directory: "~/tmp"
command: |
# use the bundled quickstart to check against what the user will download
unzip -d js-customer-registry ~/project/docs/build/src/managed/modules/javascript/attachments/js-customer-registry-quickstart.zip
cd js-customer-registry
export KALIX_NPMJS_CODEGEN_BINARY="${HOME}/project/codegen/js-gen-cli/target/native-image/kalix-codegen-js"
npm install --save \
"$HOME/project/sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz" \
"$HOME/project/npm-js/kalix-scripts/kalix-io-kalix-scripts-1.0.0.tgz" \
"$HOME/project/testkit/kalix-io-testkit-0.0.0.tgz"
npm install
npm run build
npm test
- run:
name: "Test JS Value Entity Counter sample"
command: |
Expand Down Expand Up @@ -343,6 +363,29 @@ jobs:
npm install ../sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz
DEBUG='testcontainers*' npm run integration-test
npm pack
- run:
name: "bundle quickstarts"
working_directory: "~/project/docs"
command: |
make bundles
- run:
name: "integration tests: js-customer-registry-quickstart"
working_directory: "~/tmp"
command: |
export VERSION_CHECK_ON_STARTUP=false
export KALIX_NPMJS_CODEGEN_BINARY="${HOME}/project/codegen/js-gen-cli/target/native-image/kalix-codegen-js"
source /opt/circleci/.nvm/nvm.sh
pushd ~/project/sdk && nvm install && popd
# use the bundled quickstart to check against what the user will download
unzip -d js-customer-registry ~/project/docs/build/src/managed/modules/javascript/attachments/js-customer-registry-quickstart.zip
cd js-customer-registry
npm install --save \
"$HOME/project/sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz" \
"$HOME/project/npm-js/kalix-scripts/kalix-io-kalix-scripts-1.0.0.tgz" \
"$HOME/project/testkit/kalix-io-testkit-0.0.0.tgz"
npm install
npm run build
npm run integration-test
- run:
name: "integration tests: samples/js/js-customer-registry"
command: |
Expand Down
3 changes: 2 additions & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ examples:
mkdir -p "${managed_examples}"
rsync -a --exclude-from=.examplesignore ../samples/js/js-doc-snippets/ "${managed_examples}/js-doc-snippets/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-customer-registry/ "${managed_examples}/js-customer-registry/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-customer-registry-quickstart/ "${managed_examples}/js-customer-registry-quickstart/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-valueentity-shopping-cart/ "${managed_examples}/js-valueentity-shopping-cart/"
rsync -a --exclude-from=.examplesignore ../samples/js/valueentity-counter/ "${managed_examples}/valueentity-counter/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-eventsourced-shopping-cart/ "${managed_examples}/js-eventsourced-shopping-cart/"
Expand All @@ -56,7 +57,7 @@ examples:
rsync -a --exclude-from=.examplesignore ../samples/ts/ts-replicated-entity-shopping-cart/ "${managed_examples}/ts-replicated-entity-shopping-cart/"

bundles:
bin/bundle.sh --zip "${managed_attachments}/js-customer-registry-quickstart.zip" ../samples/js/js-customer-registry
bin/bundle.sh --zip "${managed_attachments}/js-customer-registry-quickstart.zip" ../samples/js/js-customer-registry-quickstart
bin/bundle.sh --zip "${managed_attachments}/ts-customer-registry-quickstart.zip" ../samples/ts/ts-customer-registry
bin/bundle.sh --zip "${managed_attachments}/js-eventsourced-shopping-cart.zip" ../samples/js/js-eventsourced-shopping-cart
bin/bundle.sh --zip "${managed_attachments}/ts-eventsourced-shopping-cart.zip" ../samples/ts/ts-eventsourced-shopping-cart
Expand Down
2 changes: 1 addition & 1 deletion docs/config/validate-links.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ignorePatterns": [
{ "pattern": "^https://mvnrepository\\.com" },
{ "pattern": "^http://127.0.0.1:8080" },
{ "pattern": "^https://www.npmjs.com/org/kalix-io", "why": "npmjs having a bad day 2022-06-07" }
]
}
2 changes: 1 addition & 1 deletion docs/dev/src/modules/ROOT/partials/include.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:minimum_docker_version: 19.03
:minimum_docker_version: 20.10.8

:tab-icon: image:ROOT:new-tab.svg[width=12]
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
= Quickstart: Customer Registry in JavaScript

include::ROOT:partial$include.adoc[]
include::javascript:partial$attributes.adoc[]

Learn how to create a customer registry in JavaScript, package it into a container, and run it on Kalix.

== Before you begin

* If you're new to Kalix, https://console.kalix.io[create an account{tab-icon}, window="new"] so you can try it out for free.
* You'll also need to install the https://docs.kalix.io/kalix/install-kalix.html[Kalix CLI, window="new-doc"] to deploy from a terminal window.
* For this quickstart, you'll also need
** https://docs.docker.com/engine/install[Docker {minimum_docker_version} or higher, window="new"]
** https://nodejs.org/en/download/[Node.js {minimum_node_version}, window="new"]
** https://github.com/fullstorydev/grpcurl#installation[`grpcurl`, window="new"]

[NOTE]
====
If you want to bypass writing code and jump straight to the deployment:

. Download the source code using the Kalix CLI:
+
[source,command line]
----
kalix quickstart download customer-registry-javascript
----

. Skip to <<Package and deploy your service>>.
====

== Writing the Customer Registry

. From the command line, create a directory with the basic structure for your project using a template:
+
[source,command line]
----
npx @kalix-io/create-kalix-entity@latest customer-registry --template basic
----

. Change into the project directory:
+
[source,command line]
----
cd customer-registry
----

. Download and install project dependencies:
+
[source,command line]
----
npm install
----

== Define the external API

The Customer Registry service will create or retrieve a customer, including their name, email, and mailing address. The `customer_api.proto` will contain the external API your clients will invoke.

. Create a `proto` directory.
+
[source,command line]
----
mkdir proto
----

. Create a `customer_api.proto` file and save it in the `proto` directory.

. Add declarations for:
+
* The protobuf syntax version, `proto3`.
* The package name, `customer.api`.
* Import `google/protobuf/empty.proto` and Kalix `kalix/annotations.proto`.
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=declarations]
----

. Add the service endpoint. The service endpoint is annotated with `kalix.codegen` indicating we want to generate a Value Entity for this service.
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=service]
----

. Add messages to define the fields that comprise a `Customer` object (and its compound `Address`):
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=messages]
----

. Add the message that will identify which customer to retrieve for the `GetCustomer` message:
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=method-messages]
----

== Define the domain model

The `customer_domain.proto` contains all the internal data objects (https://docs.kalix.io/reference/glossary.html#entity[Entities, window="new"]). The https://docs.kalix.io/reference/glossary.html#value_entity[Value Entity, window="new"] in this quickstart is a Key/Value store that stores only the latest updates.

. Create a `customer_domain.proto` file and save it in the `proto` directory.

. Add declarations for the proto syntax and domain package.
+
[source,proto,indent=0]
.proto/customer_domain.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_domain.proto[tag=declarations]
----

. Add the `CustomerState` message with fields for the customer data, and the `Address` message:
+
[source,proto,indent=0]
.proto/customer_domain.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_domain.proto[tag=domain]
----

. Run the `build` script from the project root directory to generate source classes, based on the protobuf definitions, in which you can add the business logic:
+
[source,command line]
----
npm run build
----

== Create command handlers

Command handlers, as the name suggests, handle incoming requests before persisting them.

. If it's not open already, open the generated `src/customer.js` file for editing.

. Modify the `Create` handler by adding the logic to handle the command. The complete function should include the following:
+
[source,javascript,indent=0]
.src/customer.js
----
include::example$js-customer-registry-quickstart/src/customer.js[tag=create]
----
+
* The incoming message contains the request data from your client and the command handler updates the state of the customer.

. Modify the `GetCustomer` handler as follows to handle the `GetCustomerRequest` command:
+
[source, javascript, indent=0]
.src/customer.js
----
include::example$js-customer-registry-quickstart/src/customer.js[tag=getCustomer]
----
+
* If that customer doesn't exist, processing the command fails.
* If the customer exists, the reply message contains the customer's information.
* The conversion between the domain CustomerState and the external API is straightforward, as they have the same fields.

[NOTE]
====
The `src/index.js` file already contains the required code to start your service and register it with Kalix.
====

== Package and deploy your service

To build and publish the container image and then deploy the service, follow these steps:

. If you haven't done so yet, sign in to your Kalix account. If this is your first time using Kalix, this will let you register an account, https://docs.kalix.io/projects/create-project.html[create your first project], and set this project as the default.
+
[source,command line]
----
kalix auth login
----

. Update the `config.dockerImage` setting in the `package.json` file with your container registry.

. Use the `deploy` script to build the container image, publish it to the container registry as configured in the `package.json` file, and then automatically https://docs.kalix.io/services/deploy-service.html#_deploy[deploy the service] to Kalix using `kalix`:
+
[source,command line]
----
npm run deploy
----

. You can https://docs.kalix.io/services/deploy-service.html#_verify_service_status[verify the status of the deployed service] using:
+
[source,command line]
----
kalix service list
----

== Invoke your service

Once the service has started successfully, you can https://docs.kalix.io/services/invoke-service.html#_testing_and_development[start a proxy locally] to access the service:

[source,command line]
----
kalix service proxy customer-registry --grpcui
----

The `--grpcui` option also starts and opens a https://docs.kalix.io/services/invoke-service.html#_using_the_built_in_graphical_client[gRPC web UI] for exploring and invoking the service (available at http://127.0.0.1:8080/ui/).

Or you can use command line gRPC or HTTP clients, such as `grpcurl` or `curl`, to invoke the service through the proxy at `localhost:8080`, using plaintext connections.

A customer can be created using the `Create` method on `CustomerService`, in the gRPC web UI, or with `grpcurl`:

[source,command line]
----
grpcurl \
-d '{
"customer_id": "abc123",
"email": "someone@example.com",
"name": "Someone",
"address": {
"street": "123 Some Street",
"city": "Somewhere"
}
}' \
--plaintext localhost:8080 \
customer.api.CustomerService/Create
----

The `GetCustomer` method can be used to retrieve this customer, in the gRPC web UI, or with `grpcurl`:

[source,command line]
----
grpcurl \
-d '{"customer_id": "abc123"}' \
--plaintext localhost:8080 \
customer.api.CustomerService/GetCustomer
----

You can https://docs.kalix.io/services/invoke-service.html#_exposing_services_to_the_internet[expose the service to the internet]. A generated hostname will be returned from the expose command:

[source,command line]
----
kalix service expose customer-registry
----

== Next steps

* You can learn more about xref:javascript:value-entity.adoc[Value Entities].
* Do another https://docs.kalix.io/quickstart/sc-eventsourced-entity-javascript.html[Quickstart] to learn about Event Sourcing and xref:javascript:eventsourced.adoc[Event Sourced Entities].
5 changes: 5 additions & 0 deletions samples/js/js-customer-registry-quickstart/.bundleignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.gitignore
lib
node_modules
package-lock.json
user-function.desc
5 changes: 5 additions & 0 deletions samples/js/js-customer-registry-quickstart/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/README.md
/docker-compose.yml
/lib/generated
/node_modules
/user-function.desc
5 changes: 5 additions & 0 deletions samples/js/js-customer-registry-quickstart/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/lib/generated
/node_modules
/user-function.desc
# test quickstarts without lock file
/package-lock.json
Comment on lines +4 to +5
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not committing the package lock file for this sample, so that testing is similar to user download.

48 changes: 48 additions & 0 deletions samples/js/js-customer-registry-quickstart/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This Dockerfile uses multi-stage build process.
# See https://docs.docker.com/develop/develop-images/multistage-build/

# Stage 1: Downloading dependencies and building the application
FROM node:14.19-buster-slim AS builder

RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*

# Set the working directory
WORKDIR /home/node

# Install app dependencies
COPY package*.json ./
RUN npm ci

# Copy sources and build the app
COPY --chown=node . .
RUN npm run build

# Remove dev packages
# (the rest will be copied to the production image at stage 2)
RUN npm prune --production

# Stage 2: Building the production image
FROM node:14.19-buster-slim

# Set the working directory
WORKDIR /home/node

# Copy dependencies
COPY --from=builder --chown=node /home/node/node_modules node_modules/

# Copy the app
COPY --from=builder --chown=node \
/home/node/package*.json \
/home/node/user-function.desc \
./
COPY --from=builder --chown=node /home/node/proto ./proto
COPY --from=builder --chown=node /home/node/src ./src
COPY --from=builder --chown=node /home/node/lib ./lib

# Run the app as an unprivileged user for extra security.
USER node

# Run
EXPOSE 8080
# Call node directly to get SIGTERM for graceful shutdown
CMD ["node", "src/index.js"]
Loading