<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jonas Birmé</title>
    <description>The latest articles on Forem by Jonas Birmé (@birme).</description>
    <link>https://forem.com/birme</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F387228%2F6f166c2e-cba9-4e7c-a98a-9ce2cf53c655.jpg</url>
      <title>Forem: Jonas Birmé</title>
      <link>https://forem.com/birme</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/birme"/>
    <language>en</language>
    <item>
      <title>Run Claude Code Agents on a Schedule in the Cloud</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Wed, 15 Apr 2026 13:12:23 +0000</pubDate>
      <link>https://forem.com/oscdev/run-claude-code-agents-on-a-schedule-in-the-cloud-2lcl</link>
      <guid>https://forem.com/oscdev/run-claude-code-agents-on-a-schedule-in-the-cloud-2lcl</guid>
      <description>&lt;p&gt;You use Claude Code locally. It reviews code, writes tests, debugs problems. You close the laptop and the work stops.&lt;/p&gt;

&lt;p&gt;What if it kept going?&lt;/p&gt;

&lt;p&gt;My Agent Tasks is a new feature in &lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=my-agent-tasks" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt; (OSC) that lets you schedule Claude Code to run autonomously in the cloud, on your repositories, on a cron schedule, without your machine being on. Daily code reviews, automated health checks, weekly dependency audits, scheduled deployment reports. You define the task once. The agent does the work.&lt;/p&gt;

&lt;p&gt;I built this on top of &lt;code&gt;claude-runner&lt;/code&gt;, an open source service I developed that runs headless Claude Code sessions as one-shot jobs in containers. The scheduling layer, credentials management, and web UI shipped on April 14, 2026 as My Agent Tasks in the OSC console.&lt;/p&gt;

&lt;p&gt;Everything is open source. No proprietary agent runtime, no black-box infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is for
&lt;/h2&gt;

&lt;p&gt;Claude Code in your IDE is interactive: you type, it responds, you iterate. That model is great for active development sessions. It breaks down for anything that should happen automatically.&lt;/p&gt;

&lt;p&gt;Consider the tasks you actually want running on a schedule:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review the &lt;code&gt;src/&lt;/code&gt; directory for new security issues every morning&lt;/li&gt;
&lt;li&gt;Check that deployed services are healthy after each release&lt;/li&gt;
&lt;li&gt;Generate a weekly summary of what changed and why&lt;/li&gt;
&lt;li&gt;Update a docs file whenever the codebase drifts from it&lt;/li&gt;
&lt;li&gt;Post a Slack report after a nightly batch job completes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these need you at the keyboard. They need an agent with access to your code, the right prompt, and a timer.&lt;/p&gt;

&lt;p&gt;My Agent Tasks is that timer, attached to that agent, running in the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you start, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://app.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=my-agent-tasks" rel="noopener noreferrer"&gt;OSC account&lt;/a&gt; on a paid plan (Personal, Professional, or Business). The free tier does not include scheduled tasks.&lt;/li&gt;
&lt;li&gt;A git repository, either public or private (HTTPS URL required)&lt;/li&gt;
&lt;li&gt;An Anthropic API key&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Get your authentication credentials
&lt;/h2&gt;

&lt;p&gt;Get an API key from &lt;a href="https://console.anthropic.com" rel="noopener noreferrer"&gt;console.anthropic.com&lt;/a&gt;. Navigate to API Keys, create a new key, and copy the value.&lt;/p&gt;

&lt;p&gt;This is standard Anthropic API billing: you pay per token consumed by each task run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Navigate to My Agent Tasks
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://app.osaas.io/dashboard/agent-tasks?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=my-agent-tasks" rel="noopener noreferrer"&gt;https://app.osaas.io/dashboard/agent-tasks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first time you visit, OSC prompts you to configure your credentials. Enter your token (OAuth or API key) and click Save. The credentials are stored in Kubernetes Secrets on the platform, never exposed in API responses or logs.&lt;/p&gt;

&lt;p&gt;Once saved, you land on the task list, which is empty until you create your first task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create your first task
&lt;/h2&gt;

&lt;p&gt;Click "Create Task". You will see a form with several fields.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznw6fgl9pgdin7ua2f43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznw6fgl9pgdin7ua2f43.png" alt=" " width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Walk through them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A short identifier with no spaces. This becomes part of the job name in the system. Example: &lt;code&gt;code-review&lt;/code&gt;, &lt;code&gt;daily-health&lt;/code&gt;, &lt;code&gt;docs-sync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A plain English description of what this task does. For your own reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the instruction Claude receives. Write it the same way you would write a CLAUDE.md prompt or a Claude Code session opener. Be specific about what you want done and where results should go.&lt;/p&gt;

&lt;p&gt;Example prompt for a code review task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review the code in src/ for potential bugs, security issues, and code quality problems.
Focus on: unhandled errors, SQL injection, authentication flaws, and logic errors.
Write a summary of findings to review.md. If no issues are found, write "No issues found" with the date.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Source URL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The HTTPS URL to your git repository. Supports a branch suffix with &lt;code&gt;#&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/myorg/myrepo
https://github.com/myorg/myrepo#main
https://github.com/myorg/myrepo#staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Schedule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OSC provides presets for common schedules:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Preset&lt;/th&gt;
&lt;th&gt;Cron expression&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Every 15 minutes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*/15 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hourly&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 * * * *&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daily at 9am&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 9 * * *&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weekdays at 9am&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 9 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weekly Monday at 9am&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 9 * * 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can also enter any valid cron expression directly for custom schedules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced settings
&lt;/h3&gt;

&lt;p&gt;Expand the advanced panel for these options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Defaults to &lt;code&gt;claude-sonnet-4-5-20250514&lt;/code&gt;. Change this if you want a different Claude version. Use a less capable model for simple tasks to reduce token usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Max Turns&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Defaults to 25. This is how many back-and-forth steps Claude can take in a single run. For straightforward tasks like "generate a report", 10 is plenty. For complex tasks involving multiple files and tool calls, raise it. Higher values cost more tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allowed Tools / Disallowed Tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default Claude has access to all standard tools: file read/write, bash execution, web fetch. Restrict this for safety.&lt;/p&gt;

&lt;p&gt;For read-only analysis runs, add &lt;code&gt;Edit,Write,Bash&lt;/code&gt; to Disallowed Tools. The agent can read files and produce output but cannot modify anything.&lt;/p&gt;

&lt;p&gt;For tasks that only need to inspect code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DisallowedTools: &lt;code&gt;Edit,Write,Bash&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For tasks that should write files but not run code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DisallowedTools: &lt;code&gt;Bash&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sub Path&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your repository is a monorepo, set this to the subdirectory where Claude should work. Example: &lt;code&gt;packages/backend&lt;/code&gt; or &lt;code&gt;apps/web&lt;/code&gt;. Claude's working directory will be set to this path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For private repositories, provide a GitHub Personal Access Token (or equivalent). Stored as a Kubernetes Secret.&lt;/p&gt;

&lt;p&gt;To reference a stored secret instead of a raw token value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{secrets.mygithubtoken}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the section on the CLI below for how to store secrets this way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OSC Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Required only if your task needs to manage OSC services from within the agent session. For example, a task that provisions a new service instance or queries the catalog. This enables MCP integration from within the task. Leave empty for tasks that only touch your repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Run and monitor
&lt;/h2&gt;

&lt;p&gt;After saving, your task appears in the list with its schedule and status.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run Now&lt;/strong&gt;: Triggers an immediate execution without waiting for the next scheduled time. Useful for testing your task before relying on the schedule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last Run / Next Run&lt;/strong&gt;: Shows when the task last executed and when it is scheduled to run next. The scheduler polls every 60 seconds, so the next run timestamp is accurate within a minute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit / Delete&lt;/strong&gt;: Standard task management. Editing a task takes effect on the next scheduled run.&lt;/p&gt;

&lt;p&gt;To see what happened during a run, check the logs for the underlying &lt;code&gt;claude-runner&lt;/code&gt; job instance. You can find these in the OSC service catalog under the &lt;code&gt;birme-claude-runner&lt;/code&gt; service, or query via the CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the CLI
&lt;/h2&gt;

&lt;p&gt;If you prefer to manage tasks from the terminal, the OSC CLI exposes the underlying &lt;code&gt;claude-runner&lt;/code&gt; service directly.&lt;/p&gt;

&lt;p&gt;Install and authenticate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OSC_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-osc-token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; @osaas/cli create birme-claude-runner codereview &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;AnthropicApiKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{{secrets.anthropicapikey}}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;SourceUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/myorg/myrepo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;Prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Review the code in src/ for potential bugs and security issues. Write a summary report to review.md."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;MaxTurns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"10"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{{secrets.name}}&lt;/code&gt; syntax tells the runner to pull the value from your stored secrets at runtime, rather than embedding credentials in plain text in the job definition.&lt;/p&gt;

&lt;p&gt;Store a secret first using the web console or via the CLI. Then reference it with &lt;code&gt;{{secrets.anthropicapikey}}&lt;/code&gt; in any job option. This applies to git tokens, API keys, Slack webhook URLs, and any other sensitive value you need injected at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using MCP tools for an AI-native workflow
&lt;/h2&gt;

&lt;p&gt;If you have the &lt;a href="https://www.osaas.io/mcp?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=my-agent-tasks" rel="noopener noreferrer"&gt;OSC MCP server&lt;/a&gt; connected to Claude or another MCP-compatible tool, you can manage agent tasks through natural language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a daily task called sync-report that runs every weekday at 9am.
It should clone https://github.com/myorg/myrepo and run:
"Generate a daily standup summary from git log since yesterday.
Write it to standup.md."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;List my agent tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run my code-review task now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server maps these requests to dedicated agent task tools: &lt;code&gt;setup-agent&lt;/code&gt;, &lt;code&gt;create-agent-task&lt;/code&gt;, &lt;code&gt;list-agent-tasks&lt;/code&gt;, &lt;code&gt;run-agent-task&lt;/code&gt;, and &lt;code&gt;delete-agent-task&lt;/code&gt;. You are managing cloud infrastructure through conversation, without touching the UI or CLI at all.&lt;/p&gt;

&lt;p&gt;The OSC MCP endpoint is &lt;code&gt;https://mcp.osaas.io/mcp&lt;/code&gt;. Connect it from Claude Desktop, Cursor, or any MCP-compatible client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Injecting environment variables with a parameter store
&lt;/h2&gt;

&lt;p&gt;Some tasks need credentials beyond the git token and Claude auth: a Slack API token, a webhook URL, a database connection string. You do not want these hardcoded in your prompt or repository. OSC solves this with a parameter store (the Application Config Service).&lt;/p&gt;

&lt;p&gt;When you set the &lt;strong&gt;Config Service&lt;/strong&gt; field on a task, Claude Runner loads all key-value pairs from that parameter store and injects them as environment variables before the session starts. Your prompt can then reference them naturally, and any Bash commands Claude runs will have access to them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting it up
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Create a parameter store&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can do this through the OSC web console, the CLI, or via MCP. Using the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; @osaas/cli create eyevinn-app-config-svc myagentconfig &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;RedisUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-valkey-url&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the &lt;code&gt;setup-parameter-store&lt;/code&gt; MCP tool which creates both the Valkey instance and the config service in one step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Add your environment variables&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Store the keys your task needs. For example, a Slack token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"https://&amp;lt;your-config-svc-url&amp;gt;/api/v1/config/EYEVINN_SLACK_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"value": "xoxb-your-slack-bot-token"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add as many keys as you need. Each one becomes an environment variable in the runner container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Reference it in your task&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When creating or editing a task, set the &lt;strong&gt;Config Service&lt;/strong&gt; field to the name of your parameter store (e.g., &lt;code&gt;myagentconfig&lt;/code&gt;). You also need an &lt;strong&gt;OSC Access Token&lt;/strong&gt; set, since the runner uses it to read the parameter store at startup.&lt;/p&gt;

&lt;p&gt;Now your prompt can reference the injected variables. Here is a real example I use for scheduled deployment updates with Slack notifications:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Restart myapps intercom, mediaconvert, analytics, opensubtitlesite so it
fetches latest code. Post to slack channel #osaas-agents using the
environment variable EYEVINN_SLACK_TOKEN when done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent restarts the apps using OSC MCP tools, then posts a summary to Slack using the token that was injected from the parameter store. No credentials in the prompt, no secrets in the repository.&lt;/p&gt;

&lt;p&gt;This pattern works for any external service: post to Discord, update a Notion page, trigger a webhook, write to a database. Store the credentials in the parameter store, reference them in the prompt by environment variable name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical use cases
&lt;/h2&gt;

&lt;p&gt;Here are four patterns that work well with My Agent Tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated code review
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Schedule&lt;/strong&gt;: Weekdays at 7am, before standup&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Max Turns&lt;/strong&gt;: 15&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review the diff since yesterday (use git log and git diff). Look for:
- Obvious bugs or unhandled edge cases
- Missing error handling
- Security issues (injection, auth flaws, hardcoded secrets)
Write a concise review to daily-review.md with severity labels: INFO, WARNING, CRITICAL.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives your team a morning code review without a human having to do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduled deployment health check
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Schedule&lt;/strong&gt;: Every hour&lt;br&gt;&lt;br&gt;
&lt;strong&gt;DisallowedTools&lt;/strong&gt;: &lt;code&gt;Edit,Write&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;OSC Access Token&lt;/strong&gt;: set&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Check&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;OSC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;services&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;MCP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tools.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;healthy,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;error.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stdout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;keys:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;healthy,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;degraded,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;failed.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With an OSC Access Token set, the agent can call OSC MCP tools from inside the task to query your live service instances.&lt;/p&gt;

&lt;h3&gt;
  
  
  Daily dependency audit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Schedule&lt;/strong&gt;: Weekly Monday at 8am&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Max Turns&lt;/strong&gt;: 20&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run npm audit (or equivalent for this project) and check package.json for outdated dependencies.
Produce a report in audit.md listing: critical vulnerabilities, high vulnerabilities, and packages more than 2 major versions behind.
For each critical finding, suggest the upgrade command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Read-only analysis with restricted tools
&lt;/h3&gt;

&lt;p&gt;For tasks where you want analysis only with no risk of the agent modifying your codebase:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DisallowedTools&lt;/strong&gt;: &lt;code&gt;Edit,Write,Bash&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Prompt&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Analyze&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;TypeScript&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;identify&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="nx"&gt;patterns&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;suggest&lt;/span&gt; &lt;span class="nx"&gt;missing&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="nx"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;incorrect&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="nx"&gt;overly&lt;/span&gt; &lt;span class="nx"&gt;broad&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;Output&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;safety&lt;/span&gt; &lt;span class="nx"&gt;summary&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt; &lt;span class="nx"&gt;listing&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;finding&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Bash disabled and Edit/Write disabled, the agent reads files and reasons about them. Nothing runs. Nothing changes. Safe to run against production codebases.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;claude-runner&lt;/code&gt; service clones your repository into an ephemeral container, then runs Claude Code non-interactively with your prompt and configuration. The container has a full file system, network access (controlled by your tool settings), and the Claude Code runtime.&lt;/p&gt;

&lt;p&gt;The scheduler in OSC checks tasks every 60 seconds and spawns a new job container when a task is due. Each run is isolated: previous run state does not carry over unless your task explicitly reads output files committed back to the repo.&lt;/p&gt;

&lt;p&gt;Credentials are stored as Kubernetes Secrets in the platform namespace. They are never returned in API responses, never logged, and never included in job definitions as plain text.&lt;/p&gt;

&lt;p&gt;The runner, the scheduler, and the platform infrastructure are all open source. No proprietary agent runtime sits between you and Claude.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing your repository for agent tasks
&lt;/h2&gt;

&lt;p&gt;The quality of the agent's output depends heavily on the context it has. Before relying on scheduled tasks, invest a few minutes in your repository's context files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add a CLAUDE.md file&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the most important thing you can do. CLAUDE.md is loaded by Claude Code at the start of every session. Put in it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What this project does (one paragraph)&lt;/li&gt;
&lt;li&gt;Tech stack and key dependencies&lt;/li&gt;
&lt;li&gt;How to run tests&lt;/li&gt;
&lt;li&gt;Common patterns and conventions specific to your codebase&lt;/li&gt;
&lt;li&gt;Things Claude should NOT do (e.g., "never commit directly to main")
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# my-api&lt;/span&gt;

A REST API built with Node.js, Express, and Postgres.

&lt;span class="gu"&gt;## Testing&lt;/span&gt;
npm test - runs the full test suite
npm run test:unit - unit tests only

&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; All errors must be wrapped in AppError class
&lt;span class="p"&gt;-&lt;/span&gt; Never use any TypeScript type
&lt;span class="p"&gt;-&lt;/span&gt; Database queries go in src/db/, never inline in routes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add .claude/settings.json for tool restrictions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can set default tool restrictions at the repo level rather than configuring them per task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"disallowedTools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This acts as a safety baseline for all agent tasks targeting this repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use sub-path for monorepos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your repo has multiple packages, set the Sub Path field to direct the agent to the relevant package. The agent's working directory will be scoped to that path.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is agentic engineering
&lt;/h2&gt;

&lt;p&gt;There is a shift happening from vibe coding (AI in your IDE, you direct everything interactively) to agentic engineering, where agents run autonomously on schedules, performing real work independently.&lt;/p&gt;

&lt;p&gt;The difference is not the capability of the model. It is where the agent lives and when it runs. An agent that only runs when you are at the keyboard is a powerful tool. An agent that runs on a schedule, in the cloud, with access to your repositories and infrastructure, is a colleague.&lt;/p&gt;

&lt;p&gt;My Agent Tasks makes the second thing practical to set up.&lt;/p&gt;

&lt;p&gt;Every component that makes this work is open source: the claude-runner service, the web runners, the scheduler, the OSC platform itself. When you schedule an agent task on OSC, you are not locked into any proprietary runtime or vendor-specific agent format. You can inspect, modify, or self-host every layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;The My Agent Tasks interface is at &lt;a href="https://app.osaas.io/dashboard/agent-tasks?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=my-agent-tasks" rel="noopener noreferrer"&gt;https://app.osaas.io/dashboard/agent-tasks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Documentation is at &lt;a href="https://docs.osaas.io" rel="noopener noreferrer"&gt;https://docs.osaas.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to talk to other developers building on OSC, the community Slack is at &lt;a href="https://slack.osaas.io" rel="noopener noreferrer"&gt;https://slack.osaas.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start with a read-only task: pick a repository, set DisallowedTools to &lt;code&gt;Edit,Write,Bash&lt;/code&gt;, write a prompt that produces a report file, and run it manually first. Once you have output you trust, turn on the schedule.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=my-agent-tasks" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt; provides 200+ open source projects as managed services with native MCP integration. Every component is open source, zero vendor lock-in. Revenue is shared with the creators of the open source projects that power the platform.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>automation</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Own Your Bluesky Identity: Self-Host a PDS on Open Source Cloud</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Mon, 16 Mar 2026 13:12:42 +0000</pubDate>
      <link>https://forem.com/oscdev/own-your-bluesky-identity-self-host-a-pds-on-open-source-cloud-48ep</link>
      <guid>https://forem.com/oscdev/own-your-bluesky-identity-self-host-a-pds-on-open-source-cloud-48ep</guid>
      <description>&lt;h1&gt;
  
  
  Own Your Bluesky Identity: Self-Host a PDS on Open Source Cloud
&lt;/h1&gt;

&lt;p&gt;Your Bluesky account lives on a server you don't control. When Bluesky the company makes decisions about that server, you inherit them. The AT Protocol was designed to fix this — your identity is a DID (decentralized identifier) that can be anchored to a server you run. But most people never self-host because standing up a Personal Data Server is too much work.&lt;/p&gt;

&lt;p&gt;This guide gets you a running PDS in about five minutes. No Kubernetes experience required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;Your PDS is more than storage. It's the root of your AT Protocol identity. When your account lives on &lt;code&gt;bsky.social&lt;/code&gt;, your DID resolves through Bluesky's infrastructure. When you self-host, your DID resolves through yours. You can migrate back to &lt;code&gt;bsky.social&lt;/code&gt; or to any other PDS at any time — the AT Protocol guarantees portability. But you're never dependent on one company's availability or policies.&lt;/p&gt;

&lt;p&gt;For developers especially: a self-hosted PDS lets you experiment with the AT Protocol directly, create accounts for testing, and build on infrastructure you understand end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An account on &lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=bluesky-pds-guide" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt; (free tier works)&lt;/li&gt;
&lt;li&gt;A terminal (for the PDS admin steps)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. OSC handles the container, TLS, and DNS automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create the PDS instance
&lt;/h2&gt;

&lt;p&gt;There are two ways to spin up a PDS on OSC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: Use the web console&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Log in to &lt;a href="https://app.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=bluesky-pds-guide" rel="noopener noreferrer"&gt;app.osaas.io&lt;/a&gt;, find &lt;strong&gt;Bluesky Personal Data Server&lt;/strong&gt; in the service catalog, and click &lt;strong&gt;Create&lt;/strong&gt;. Fill in a name and an admin password. That's it — OSC provisions the container, TLS certificate, and DNS record for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B: Use an AI agent via MCP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Connect the &lt;a href="https://www.osaas.io/mcp?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=bluesky-pds-guide" rel="noopener noreferrer"&gt;OSC MCP server&lt;/a&gt; to Claude, ChatGPT, or any MCP-compatible AI tool. Then just tell it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Create a bluesky-social-pds instance named &lt;span class="s2"&gt;"mypds"&lt;/span&gt; with admin password &lt;span class="s2"&gt;"choose-a-strong-password"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent calls the &lt;code&gt;create-service-instance&lt;/code&gt; tool and hands you back a running URL.&lt;/p&gt;

&lt;p&gt;Either way, you'll get an instance URL in the pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://{tenant}-mypds.bluesky-social-pds.auto.prod.osaas.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hold onto that URL — it's your PDS hostname for every step below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Verify it's running
&lt;/h2&gt;

&lt;p&gt;The PDS exposes a health endpoint. Give it 30 seconds to start, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://YOUR_PDS_URL/xrpc/_health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"0.4.208"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get a connection error, wait another 30 seconds. The container is still starting.&lt;/p&gt;

&lt;p&gt;You can also check what domains and features the server advertises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://YOUR_PDS_URL/xrpc/com.atproto.server.describeServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the server DID, available user domains, and whether invite codes are required (they are, by default).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create an invite code
&lt;/h2&gt;

&lt;p&gt;The PDS requires an invite code to create accounts. This is an admin-only operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Basic &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'admin:YOUR_ADMIN_PASSWORD'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"useCount": 1}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://YOUR_PDS_URL/xrpc/com.atproto.server.createInviteCode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_ADMIN_PASSWORD&lt;/code&gt; with the password you set in Step 1.&lt;/p&gt;

&lt;p&gt;The response looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"mypds-abcd1234-xyz"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save that code — you'll need it in the next step. Set &lt;code&gt;useCount&lt;/code&gt; higher if you want the same code to work multiple times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Create your account
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "email": "you@example.com",
    "handle": "yourname.YOUR_PDS_HOSTNAME",
    "password": "your-account-password",
    "inviteCode": "mypds-abcd1234-xyz"
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://YOUR_PDS_URL/xrpc/com.atproto.server.createAccount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handle follows the pattern &lt;code&gt;username.your-pds-hostname&lt;/code&gt; — for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alice.mytenant-mypds.bluesky-social-pds.auto.prod.osaas.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response includes your DID, handle, and access tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"did"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"did:plc:..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"handle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice.mytenant-mypds..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"accessJwt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refreshJwt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your DID is your permanent identity on the AT Protocol. The handle is how people find you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Log in from the Bluesky app
&lt;/h2&gt;

&lt;p&gt;Open the Bluesky app (iOS, Android, or web at bsky.app).&lt;/p&gt;

&lt;p&gt;On the sign-in screen, look for "Advanced" or "Hosting provider" — the exact label varies by app version. Enter your PDS URL as the hosting provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://YOUR_PDS_URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then sign in with the handle and password you created in Step 4. The app will connect to your PDS directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: Use a custom domain
&lt;/h2&gt;

&lt;p&gt;The default hostname is readable but not exactly elegant. If you own a domain, you can map it to your PDS and use handles like &lt;code&gt;alice.yourdomain.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two things need to happen:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add the custom domain in OSC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the web console, go to &lt;strong&gt;My Apps&lt;/strong&gt; and add a custom domain for your PDS instance (e.g., &lt;code&gt;pds.yourdomain.com&lt;/code&gt;). You can also do this via MCP — ask your AI agent to add a custom domain for your PDS instance. This tells OSC to provision a TLS certificate and route traffic for that domain to your instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Point DNS to OSC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add a CNAME record in your DNS provider pointing &lt;code&gt;pds.yourdomain.com&lt;/code&gt; to your instance's auto-generated hostname. Once DNS propagates and the TLS certificate is issued, your PDS is reachable at the custom domain.&lt;/p&gt;

&lt;p&gt;When creating the PDS instance, set the &lt;strong&gt;DnsName&lt;/strong&gt; field to your custom domain so the PDS knows its public hostname. Handles on your PDS then become &lt;code&gt;username.pds.yourdomain.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also configure SMTP for sending verification emails by setting the &lt;strong&gt;EmailSmtpUrl&lt;/strong&gt; and &lt;strong&gt;EmailFromAddress&lt;/strong&gt; fields when creating the instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next: migrate your existing account
&lt;/h2&gt;

&lt;p&gt;If you already have a Bluesky account on &lt;code&gt;bsky.social&lt;/code&gt;, you can migrate it to your PDS. The AT Protocol supports account migration — your DID stays the same, your follows and followers transfer, and your existing posts remain accessible. The migration process is &lt;a href="https://atproto.com/guides/account-migration" rel="noopener noreferrer"&gt;documented in the AT Protocol specs&lt;/a&gt; and Bluesky has a migration tool in their settings.&lt;/p&gt;

&lt;p&gt;You don't have to do this immediately. Your new self-hosted account and your &lt;code&gt;bsky.social&lt;/code&gt; account can coexist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The infrastructure should be simple
&lt;/h2&gt;

&lt;p&gt;Running your own PDS used to mean setting up a VPS, installing Docker, managing volumes, dealing with TLS, and hoping nothing breaks on Saturday night. That friction is why most people never do it.&lt;/p&gt;

&lt;p&gt;The Bluesky PDS is available on &lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=bluesky-pds-guide" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt; because it belongs there — alongside Postgres, Valkey, MinIO, and 160+ other open source projects, all running as managed services. No configuration overhead, no infrastructure babysitting.&lt;/p&gt;

&lt;p&gt;The AT Protocol's bet is that social media built on open protocols is more durable than any single platform. The infrastructure underneath that bet should be just as open and just as easy to run.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=bluesky-pds-guide" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt; provides open source software as managed services. The Bluesky PDS is one of 160+ services available — all open source, no vendor lock-in.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bluesky</category>
      <category>selfhosted</category>
      <category>opensource</category>
      <category>atprotocol</category>
    </item>
    <item>
      <title>How to Deploy Your Vibe-Coded App to Production with Claude + OSC MCP</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Wed, 11 Mar 2026 09:36:22 +0000</pubDate>
      <link>https://forem.com/oscdev/how-to-deploy-your-vibe-coded-app-to-production-with-claude-osc-mcp-4fhk</link>
      <guid>https://forem.com/oscdev/how-to-deploy-your-vibe-coded-app-to-production-with-claude-osc-mcp-4fhk</guid>
      <description>&lt;p&gt;Vibe-coded apps built with Claude Code, Cursor, or Windsurf deploy to production in under 5 minutes using the OSC MCP server. Connect your AI agent once, then deploy databases, app hosting, and 200+ open source services through natural language — no Kubernetes, no cloud config. The agent that wrote your app can also ship it. You just need to give it the right infrastructure tools.&lt;/p&gt;

&lt;p&gt;This is the gap vibe coding exposed: the build is instant, the deploy is not. Claude can scaffold a full-stack app with a Postgres-backed API and authentication in under 10 minutes. Then you spend the next hour provisioning the database, setting environment variables, wiring the connection strings, and debugging TLS. The creative work automated, the mechanical work manual. &lt;a href="https://www.osaas.io?utm_source=blog&amp;amp;utm_medium=organic&amp;amp;utm_campaign=vibe-deployment" rel="noopener noreferrer"&gt;Open Source Cloud (OSC)&lt;/a&gt; closes that gap by giving your AI agent direct access to infrastructure tools through MCP.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is vibe deployment?
&lt;/h2&gt;

&lt;p&gt;Vibe deployment is the natural extension of vibe coding: you let the same AI agent that built your app also ship it. Instead of stopping at "here's your code," the agent provisions the database, deploys the application, sets the environment variables, and hands you a live URL — all in the same conversation. The infrastructure decisions happen through tool calls, not through config files you write and run yourself.&lt;/p&gt;

&lt;p&gt;The term is new, but the idea is straightforward: if an agent can write the code, it should be able to run it too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;One command. That's it.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Claude Code&lt;/strong&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http osc https://mcp.osaas.io/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time you use an OSC tool, Claude Code will open a browser window for OAuth authentication. If you don't have an OSC account yet, you can create one there — no separate signup step required.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Claude Desktop, Cursor, Windsurf&lt;/strong&gt;, and other MCP-compatible tools, the &lt;a href="https://www.osaas.io/mcp?utm_source=blog&amp;amp;utm_medium=organic&amp;amp;utm_campaign=vibe-deployment" rel="noopener noreferrer"&gt;OSC MCP page&lt;/a&gt; has one-step connection instructions for each.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step-by-step: deploy a vibe-coded app via Claude Code
&lt;/h2&gt;

&lt;p&gt;Say you've built a Node.js app with a PostgreSQL backend during a vibe coding session. Your agent wrote the code. Now you want it live. Here's how the conversation goes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Ask the agent to provision a database&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: I need a PostgreSQL database for this app. Can you provision one on OSC?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent calls the OSC MCP &lt;code&gt;create-service-instance&lt;/code&gt; tool with &lt;code&gt;serviceId: "eyevinn-postgres"&lt;/code&gt;. Within about 30 seconds, it gets back a connection string using the instance's port-mapped host and port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Your&lt;/span&gt; &lt;span class="n"&gt;PostgreSQL&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;@&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;host&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;
&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="s1"&gt;'ve stored it in the OSC parameter store under "DATABASE_URL".
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Deploy the app&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Now deploy the app. Source is github.com/yourname/your-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent calls &lt;code&gt;create-myapp&lt;/code&gt; with your GitHub repo URL and attaches the parameter store containing &lt;code&gt;DATABASE_URL&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Agent: Deploying from github.com/yourname/your-app...
Build complete. Your app is live at:
https://yourapp-abc123.apps.osaas.io
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Wire a custom domain (optional)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Map it to api.myproject.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent calls the domain-mapping tool, creates the DNS record, and Let's Encrypt TLS is provisioned automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Verify it's running&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Check the app status and show me the last 20 lines of logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent queries the instance health and fetches logs — no terminal, no cloud console, no context switch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Done&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From "I need a database" to a live, TLS-terminated, custom-domain app: four exchanges. The agent handled every infrastructure decision. You stayed in the conversation.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you can deploy
&lt;/h2&gt;

&lt;p&gt;OSC exposes 200+ open source services through MCP. The categories your agent can provision in a single session:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Databases&lt;/strong&gt;&lt;br&gt;
PostgreSQL, MariaDB, CouchDB, ClickHouse, MongoDB, MySQL — all fully managed, all open source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching and queues&lt;/strong&gt;&lt;br&gt;
Valkey (Redis-compatible), SmoothMQ (SQS-compatible). One tool call, connection string back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;br&gt;
S3-compatible object storage (MinIO). Your agent can create a bucket, get credentials, and wire them to your app without leaving the conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;App hosting&lt;/strong&gt;&lt;br&gt;
Deploy any GitHub repo running Node.js, Python, or WASM. Environment variables wired via the OSC parameter store. Custom domains with automatic TLS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intelligence&lt;/strong&gt;&lt;br&gt;
The OSC Architect tool — an AI that helps your agent design multi-service architectures before it starts provisioning. Ask it to plan before you build.&lt;/p&gt;

&lt;p&gt;The full catalog: &lt;a href="https://www.osaas.io/mcp?utm_source=blog&amp;amp;utm_medium=organic&amp;amp;utm_campaign=vibe-deployment" rel="noopener noreferrer"&gt;osaas.io/mcp&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  No lock-in — take your code anywhere
&lt;/h2&gt;

&lt;p&gt;Other platforms deploy to their infrastructure. You write your app against their APIs, their managed runtimes, their proprietary services. When agents make infrastructure decisions at speed, you can end up locked into vendors you didn't consciously choose.&lt;/p&gt;

&lt;p&gt;OSC deploys to open source. Every service your agent provisions is an open source project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL is actual PostgreSQL&lt;/li&gt;
&lt;li&gt;Storage is MinIO-compatible S3&lt;/li&gt;
&lt;li&gt;Caching is Valkey — the Redis fork maintained by the Linux Foundation&lt;/li&gt;
&lt;li&gt;App hosting runs on open source container runtimes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means you can take any component — or the entire stack — and run it yourself on AWS, GCP, bare metal, or your own Kubernetes cluster. The connection strings work the same. The data is yours.&lt;/p&gt;

&lt;p&gt;When your AI agent is making infrastructure choices for you, open source is your insurance policy.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this looks like at scale
&lt;/h2&gt;

&lt;p&gt;The production benchmark: &lt;a href="https://www.osaas.io/examples/streaming-tech-tvplus?utm_source=blog&amp;amp;utm_medium=organic&amp;amp;utm_campaign=vibe-deployment" rel="noopener noreferrer"&gt;Streaming Tech TV+&lt;/a&gt;, a streaming platform built in 36 hours by one developer directing 6 AI agents. 15,000+ lines of code, 99 commits, 13 OSC services in the final stack — PostgreSQL, two Valkey caches, MinIO, video transcoding, HLS packaging, Whisper subtitles, ClickHouse analytics. The agents didn't just write code. They provisioned every service and wired every connection.&lt;/p&gt;

&lt;p&gt;That's the endpoint. Your next vibe coding session is the starting point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;claude mcp add --transport http osc https://mcp.osaas.io/mcp&lt;/code&gt; (or &lt;a href="https://www.osaas.io/mcp?utm_source=blog&amp;amp;utm_medium=organic&amp;amp;utm_campaign=vibe-deployment" rel="noopener noreferrer"&gt;connect your tool of choice&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Authenticate via OAuth — account creation is part of the flow if you're new&lt;/li&gt;
&lt;li&gt;Start a new session, ask your agent to build and deploy something&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next time you vibe code an app, you don't have to stop when the code is done.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://www.osaas.io?utm_source=blog&amp;amp;utm_medium=organic&amp;amp;utm_campaign=vibe-deployment" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt; provides 200+ open source projects as managed services with native MCP integration. Every component is open source — zero vendor lock-in. Revenue is shared with the creators of the open source projects that power the platform.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Infrastructure-as-Code Was Built for Humans. AI Agents Need Infrastructure-as-Tools.</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Wed, 04 Mar 2026 15:48:12 +0000</pubDate>
      <link>https://forem.com/oscdev/infrastructure-as-code-was-built-for-humans-ai-agents-need-infrastructure-as-tools-578e</link>
      <guid>https://forem.com/oscdev/infrastructure-as-code-was-built-for-humans-ai-agents-need-infrastructure-as-tools-578e</guid>
      <description>&lt;h1&gt;
  
  
  Infrastructure-as-Code Was Built for Humans. AI Agents Need Infrastructure-as-Tools.
&lt;/h1&gt;

&lt;p&gt;Last week I watched a Claude agent write a complete Next.js app with a Postgres-backed API, authentication, and tests. Took about 8 minutes. Then it said "here's your code" and I spent the next hour manually provisioning the database, configuring the deployment, setting environment variables, and debugging TLS.&lt;/p&gt;

&lt;p&gt;The agent did the creative, hard work. I did the mechanical, boring work. Something is backwards here.&lt;/p&gt;

&lt;p&gt;This isn't about agents getting smarter. It's about infrastructure not speaking their language yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap nobody's solving
&lt;/h2&gt;

&lt;p&gt;Look at the last mile of every AI coding session:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent writes the code &lt;em&gt;(automated)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You provision a database &lt;em&gt;(manual)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You set up hosting &lt;em&gt;(manual)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You configure a domain + TLS &lt;em&gt;(manual)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You wire environment variables &lt;em&gt;(manual)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;You deploy and debug &lt;em&gt;(manual)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 2-6 aren't creative work. They're mechanical, repetitive, and well-defined — exactly the kind of tasks agents are good at. But agents can't do them because infrastructure tools weren't designed for tool-calling.&lt;/p&gt;

&lt;p&gt;Terraform assumes a human writes HCL files, runs &lt;code&gt;terraform plan&lt;/code&gt;, reviews the diff, runs &lt;code&gt;terraform apply&lt;/code&gt;, and manages state files. Pulumi assumes you're writing a program. Even cloud CLIs assume you're typing commands in a terminal.&lt;/p&gt;

&lt;p&gt;An agent doesn't want to write a Terraform file. It wants to call &lt;code&gt;create-database&lt;/code&gt; and get a connection string back.&lt;/p&gt;

&lt;h2&gt;
  
  
  What agent-native infrastructure looks like
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=agents-need-infrastructure" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt;, we've built what we think this layer should be: infrastructure exposed as &lt;a href="https://www.osaas.io/mcp?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=agents-need-infrastructure" rel="noopener noreferrer"&gt;MCP tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;MCP (&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;) is the open standard for giving AI agents access to external tools. It's already baked into Claude, ChatGPT, Cursor, Copilot, Windsurf, and most AI coding assistants. When you connect our MCP server, the agent gets 40+ tools for provisioning real infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Databases&lt;/strong&gt;: Postgres, MariaDB, CouchDB, ClickHouse, Valkey (Redis-compatible)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: S3-compatible object storage buckets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applications&lt;/strong&gt;: Deploy any Git repo (GitHub, Gitea, GitLab) as a running service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration&lt;/strong&gt;: Parameter stores, secrets management, environment variables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domains&lt;/strong&gt;: Custom domain mapping with automatic TLS via Let's Encrypt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intelligence&lt;/strong&gt;: An OSC Architect AI that helps design multi-service architectures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tool is a single function call with structured JSON input/output. No config files. No state management. No plan/apply cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent: "I need a Postgres database for the user service"
→ calls create-database(type: "postgres", name: "userdb")
→ gets back: { url: "postgres://...", port: 5432, status: "running" }
→ stores the connection string in a parameter store
→ deploys the app with the parameter store attached
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four tool calls. The agent doesn't need to know what Kubernetes is.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But Vercel and Railway have MCP servers too"
&lt;/h2&gt;

&lt;p&gt;They do. And you should look at what those MCP servers actually expose.&lt;/p&gt;

&lt;p&gt;Vercel's MCP server lets your agent read logs, inspect project metadata, and browse documentation. Railway's lets you query metrics and manage environments. These are useful, but they're read-only — dashboard operations wrapped in a protocol.&lt;/p&gt;

&lt;p&gt;OSC's MCP server exposes write operations. Create a database. Deploy an application. Provision storage. Wire a domain. Set a secret. Your agent doesn't just monitor your infrastructure — it builds it.&lt;/p&gt;

&lt;p&gt;That's the difference between giving an agent a window and giving it a workbench.&lt;/p&gt;

&lt;h2&gt;
  
  
  From 36 hours of agents to production
&lt;/h2&gt;

&lt;p&gt;This isn't a thought experiment. &lt;a href="https://www.osaas.io/examples/streaming-tech-tvplus?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=agents-need-infrastructure" rel="noopener noreferrer"&gt;Streaming Tech TV+&lt;/a&gt; is a production streaming platform built in 36 hours by one developer (Jonas Birme) directing 6 AI agents. The architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 Claude Opus 4 agents: team lead + architect&lt;/li&gt;
&lt;li&gt;1 Claude Opus 4 agent: UX designer&lt;/li&gt;
&lt;li&gt;3 Claude Sonnet 4 agents: backend, frontend, QA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;13 OSC services in the final stack: PostgreSQL, two Valkey caches, MinIO object storage, video transcoding (SVT Encore), HLS packaging, auto-generated subtitles (Whisper), ClickHouse analytics, plus the app frontends and backends.&lt;/p&gt;

&lt;p&gt;15,000+ lines of code. 76 files. 99 commits. ~150 agent-hours of work.&lt;/p&gt;

&lt;p&gt;The agents didn't just write code. They provisioned databases, created storage buckets, deployed services, and wired event pipelines. The human directed the architecture. The agents handled everything else — including infrastructure.&lt;/p&gt;

&lt;p&gt;Other &lt;a href="https://www.osaas.io/examples?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=agents-need-infrastructure" rel="noopener noreferrer"&gt;community examples&lt;/a&gt; show the same pattern across different domains: a CRM (SpecterCRM), a social reading platform (PageTurner), an audio social network (VoiceCircle), an RSS curation pipeline, a gaming activity tracker. All built with AI assistance, all deployed on OSC infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open source is non-negotiable for agent-provisioned infra
&lt;/h2&gt;

&lt;p&gt;Here's a scenario that should worry you: your agent spins up three proprietary managed services during a conversation. You didn't explicitly choose those vendors — the agent picked what was available. Six months later, you realize you're paying 3x what you expected and migration means rewriting your data layer.&lt;/p&gt;

&lt;p&gt;On OSC, every service is open source. Postgres is actual Postgres. Storage is MinIO-compatible. Caching is Valkey (the Redis fork). Your Next.js app runs on an open source web runner container. You can take any component — or the whole stack — and move it to AWS, GCP, bare metal, or your laptop.&lt;/p&gt;

&lt;p&gt;When agents make infrastructure decisions at speed, open source is your safety net. Not a philosophical preference — a practical one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The infrastructure-as-code to infrastructure-as-tools shift
&lt;/h2&gt;

&lt;p&gt;Infrastructure-as-code was the right abstraction for human developers. Declarative files. Version control. Code review. Plan/apply. It fits how humans think about systems.&lt;/p&gt;

&lt;p&gt;Agents think differently. They need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immediate feedback&lt;/strong&gt;: Call a tool, get a result in seconds. Not "write a file, run a command, wait for convergence, parse the output."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic operations&lt;/strong&gt;: One tool does one thing. Not "here's a 200-line manifest that creates 15 resources and might partially fail."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured I/O&lt;/strong&gt;: JSON in, JSON out. URLs, connection strings, status codes. Not log streams to regex-parse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This doesn't mean IaC goes away — it's still the right choice for version-controlled, team-reviewed infrastructure changes. But there's a new layer emerging on top: one that speaks the agent's language while orchestrating the same underlying infrastructure. The Terraform MCP server is one approach (wrapping IaC in tool calls). OSC takes a different approach: the tools are the infrastructure API directly, no IaC intermediary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes
&lt;/h2&gt;

&lt;p&gt;When agents can provision infrastructure natively, development workflows shift:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prototyping becomes a conversation.&lt;/strong&gt; "Build me a REST API with a Postgres backend and deploy it" goes from a day of setup to a 5-minute chat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The feedback loop collapses.&lt;/strong&gt; Agent writes code → deploys → hits an error → reads logs → fixes → redeploys. No human in the loop for the mechanical steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full-stack is the default.&lt;/strong&gt; When a database is one tool call away, there's no reason for an agent to stop at "here's your code, now go set up a database yourself."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The barrier drops to intent.&lt;/strong&gt; If you can describe what you want, an agent can build and deploy it. The infrastructure knowledge lives in the tools, not in your head.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Connect the &lt;a href="https://www.osaas.io/mcp?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=agents-need-infrastructure" rel="noopener noreferrer"&gt;OSC MCP server&lt;/a&gt; to Claude Desktop, Cursor, Copilot, or any MCP-compatible tool. Then don't ask it to "write code for" something. Ask it to &lt;strong&gt;build&lt;/strong&gt; something. Database, backend, frontend, deployed, live.&lt;/p&gt;

&lt;p&gt;The free tier is enough to test the full workflow.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=agents-need-infrastructure" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt; provides 200+ open source projects as managed services with native MCP integration. Every component is open source — zero vendor lock-in. Revenue is shared with the creators of the open source projects that power the platform.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>infrastructure</category>
      <category>mcp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I let my AI agent deploy a Next.js app and it actually worked</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Mon, 02 Mar 2026 14:05:15 +0000</pubDate>
      <link>https://forem.com/oscdev/i-let-my-ai-agent-deploy-a-nextjs-app-and-it-actually-worked-d2n</link>
      <guid>https://forem.com/oscdev/i-let-my-ai-agent-deploy-a-nextjs-app-and-it-actually-worked-d2n</guid>
      <description>&lt;p&gt;I've been using Claude and Cursor to write code for a while now. The part that always annoyed me was the handoff. The agent writes the app, then I alt-tab to some dashboard, click through a deploy wizard, set up a database in another tab, configure env vars somewhere else. The agent did the interesting work. I got the chores.&lt;/p&gt;

&lt;p&gt;So I tried something different: I connected an MCP server to Claude that has actual infrastructure tools — not just "read your logs" tools, but tools that create databases, deploy apps, set up domains. The MCP server is from &lt;a href="https://www.osaas.io/mcp?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=deploy-nextjs" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt;, and the tool I cared about was &lt;code&gt;create-my-app&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deploy
&lt;/h2&gt;

&lt;p&gt;Here's the literal prompt I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deploy my Next.js app from https://github.com/myuser/my-app
as a Node.js app called "myapp"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent called &lt;code&gt;create-my-app&lt;/code&gt;. OSC pulled the repo, detected Next.js, built it, deployed it to Kubernetes, and gave me an HTTPS URL at &lt;code&gt;*.apps.osaas.io&lt;/code&gt; with TLS already configured. Took about 30 seconds.&lt;/p&gt;

&lt;p&gt;No Dockerfile. No build config. No YAML.&lt;/p&gt;

&lt;p&gt;The app is private to your account by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened next was the interesting part
&lt;/h2&gt;

&lt;p&gt;I didn't leave the conversation. I just kept going:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a Postgres database called myappdb and set up a parameter
store with the connection string as DATABASE_URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That turned into three tool calls: &lt;code&gt;create-database&lt;/code&gt;, &lt;code&gt;setup-parameter-store&lt;/code&gt;, &lt;code&gt;set-parameter&lt;/code&gt;. Database created, connection string wired into the app's config.&lt;/p&gt;

&lt;p&gt;Then later when something broke:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Show me the last 50 error logs for my app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent called &lt;code&gt;get-my-app-logs&lt;/code&gt; with &lt;code&gt;level: error&lt;/code&gt; and &lt;code&gt;tail: 50&lt;/code&gt;. I read the error right there in the chat, fixed the code, and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Restart my app to pull the latest code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One call to &lt;code&gt;restart-my-app&lt;/code&gt;. It pulled the latest commit and restarted the container.&lt;/p&gt;

&lt;p&gt;The whole loop — code, deploy, break something, read logs, fix, redeploy — happened without switching windows. That's the part that felt genuinely different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other runtimes
&lt;/h2&gt;

&lt;p&gt;Same flow works for Python and WebAssembly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deploy my FastAPI app from GitHub as a Python app called "apiserver"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Private repos work too — you pass a GitHub token and it gets stored as a Kubernetes secret.&lt;/p&gt;

&lt;p&gt;If you don't have a repo hosted anywhere, there's a &lt;code&gt;setup-git-repo&lt;/code&gt; tool that creates a hosted Git repo you can push to and then deploy from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom domains
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Map myapp.example.com to my app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One call to &lt;code&gt;create-my-domain&lt;/code&gt;. TLS gets provisioned automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  On portability
&lt;/h2&gt;

&lt;p&gt;One thing worth mentioning: everything running under the hood is open source. The web runner, the database (actual Postgres), the storage (MinIO-compatible). You can take the whole stack and move it elsewhere if you want to.&lt;/p&gt;

&lt;p&gt;That matters more than usual here because the agent is making infrastructure decisions on your behalf inside a conversation. Knowing you can pull everything out and self-host it later is reassuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people have built
&lt;/h2&gt;

&lt;p&gt;Someone built &lt;a href="https://www.osaas.io/examples/streaming-tech-tvplus?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=deploy-nextjs" rel="noopener noreferrer"&gt;Streaming Tech TV+&lt;/a&gt; — a streaming platform with video transcoding, AI subtitles, analytics, and a CMS — in 36 hours using 6 AI agents across 13 services. That's 15,000 lines of code deployed to production by one person.&lt;/p&gt;

&lt;p&gt;There are more examples at &lt;a href="https://www.osaas.io/examples?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=deploy-nextjs" rel="noopener noreferrer"&gt;osaas.io/examples&lt;/a&gt; if you're curious.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sign up at &lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=deploy-nextjs" rel="noopener noreferrer"&gt;osaas.io&lt;/a&gt; (there's a free tier)&lt;/li&gt;
&lt;li&gt;Connect the &lt;a href="https://www.osaas.io/mcp?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=deploy-nextjs" rel="noopener noreferrer"&gt;MCP server&lt;/a&gt; to Claude Desktop, Cursor, Copilot, Windsurf, or whatever MCP client you use&lt;/li&gt;
&lt;li&gt;Tell your agent to deploy something&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;a href="https://www.osaas.io/my-apps?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=deploy-nextjs" rel="noopener noreferrer"&gt;My Apps&lt;/a&gt; page is where you manage your deployed apps through the web UI, if you want that.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built on &lt;a href="https://www.osaas.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=deploy-nextjs" rel="noopener noreferrer"&gt;Open Source Cloud&lt;/a&gt;. All components are open source. Revenue is shared with the projects that power the platform.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Adding user registration and authentication to your application with open web services</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Mon, 24 Feb 2025 00:47:17 +0000</pubDate>
      <link>https://forem.com/oscdev/adding-user-registration-and-authentication-to-your-application-with-open-web-services-3ff6</link>
      <guid>https://forem.com/oscdev/adding-user-registration-and-authentication-to-your-application-with-open-web-services-3ff6</guid>
      <description>&lt;p&gt;In this blog post we will show how easy you can add user registration and authentication to your application using open web services in &lt;a href="https://www.osaas.io" rel="noopener noreferrer"&gt;Eyevinn Open Source Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this guide
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a user database using an &lt;a href="https://app.osaas.io/dashboard/service/apache-couchdb" rel="noopener noreferrer"&gt;NoSQL open web service&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Launch an open web service for password authentication based on &lt;a href="https://github.com/openauthjs/openauth" rel="noopener noreferrer"&gt;OpenAuth&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Develop a simple NextJS application as an example.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An Eyevinn Open Source Cloud account. &lt;a href="https://app.osaas.io" rel="noopener noreferrer"&gt;Sign up here for free&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Email service for sending email verification codes. In this example we will use the &lt;a href="https://ethereal.email" rel="noopener noreferrer"&gt;fake SMTP service&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create the user database
&lt;/h2&gt;

&lt;p&gt;Login and navigate to the &lt;a href="https://app.osaas.io/dashboard/service/apache-couchdb" rel="noopener noreferrer"&gt;CouchDB service&lt;/a&gt; in Eyevinn Open Source Cloud web console. Create a new couchdb instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98p9lbir5c2sxuaolhwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98p9lbir5c2sxuaolhwy.png" alt="Create CouchDB" width="460" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the instance card and open the web user interface for the database. In the user interface create a database that we can call &lt;code&gt;users&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxmvhig9f6jb1wfhae77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxmvhig9f6jb1wfhae77.png" alt="User database" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note down the URL to the database instance, in our example it is &lt;code&gt;https://eyevinnlab-blog.apache-couchdb.auto.prod.osaas.io&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup a fake SMTP mailer
&lt;/h2&gt;

&lt;p&gt;For the purpose of you to be able to follow this example without having access to a real SMTP server for outgoing email you can create a fake SMTP mailer at &lt;a href="https://ethereal.email/" rel="noopener noreferrer"&gt;Ethereal Email&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create an account and note down the SMTP settings generated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjd5acews6gkwz7rs0ok9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjd5acews6gkwz7rs0ok9.png" alt="Fake SMTP mailer" width="737" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch password authentication service
&lt;/h2&gt;

&lt;p&gt;Now navigate to the &lt;a href="https://app.osaas.io/dashboard/service/eyevinn-openauth-pwd" rel="noopener noreferrer"&gt;OpenAuth Password&lt;/a&gt; open web service and press "Create authservice".&lt;/p&gt;

&lt;p&gt;Enter the following in the create dialog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;blog&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;UserDbUrl: &lt;code&gt;https://&amp;lt;db-user&amp;gt;:&amp;lt;db-password&amp;gt;@&amp;lt;db-url&amp;gt;/&amp;lt;db-name&amp;gt;&lt;/code&gt; where &lt;code&gt;&amp;lt;db-url&amp;gt;&lt;/code&gt; is the hostname for the database instance you created and &lt;code&gt;&amp;lt;db-name&amp;gt;&lt;/code&gt; the name of the database you created. In this example this is &lt;code&gt;https://admin:secret@eyevinnlab-blog.apache-couchdb.auto.prod.osaas.io/users&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SmtpMailerUrl: &lt;code&gt;smtp://&amp;lt;smtp-user&amp;gt;:&amp;lt;smtp-password&amp;gt;@&amp;lt;smtp-host&amp;gt;:&amp;lt;smtp-port&amp;gt;&lt;/code&gt;. In our example with a fake SMTP mailer we have &lt;code&gt;smtp://giovani.sporer@ethereal.email:NSjjETSjAYD7vKECF9@smtp.ethereal.email:587&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foh6snzwcukng10xi70ju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foh6snzwcukng10xi70ju.png" alt="Create password auth service" width="461" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note down the URL to the password authentication service available on the instance card. In our example it is &lt;code&gt;https://eyevinnlab-blog.eyevinn-openauth-pwd.auto.prod.osaas.io&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Develop a simple NextJS application
&lt;/h2&gt;

&lt;p&gt;Now we will develop a simple NextJS application to verify that it works. It is based on the example application found in the &lt;a href="https://github.com/openauthjs/openauth/tree/master/examples/client/nextjs" rel="noopener noreferrer"&gt;OpenAuth GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Clone the OpenAuth repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% git clone git@github.com:openauthjs/openauth.git 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a project folder for your application and copy the example code from the OpenAuth repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% &lt;span class="nb"&gt;mkdir &lt;/span&gt;my-app
% &lt;span class="nb"&gt;cd &lt;/span&gt;my-app
% &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ../openauth/examples/client/nextjs/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we copied this example from a mono repo we need to uninstall the referenced openauth library and install the published package instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm uninstall @openauthjs/openauth 
% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; @openauthjs/openauth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the other dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the file &lt;code&gt;app/auth.ts&lt;/code&gt; and replace the following in the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;subjects&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../../subjects&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSubjects&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@openauthjs/openauth/subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valibot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subjects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSubjects&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the file &lt;code&gt;app/page.tsx&lt;/code&gt; and change the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;Logged&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/code&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;Logged&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/code&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then change the &lt;code&gt;issuer&lt;/code&gt; URL &lt;code&gt;http://localhost:3000&lt;/code&gt; to the URL to the password authentication service you created, as in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;clientID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://eyevinnlab-blog.eyevinn-openauth-pwd.auto.prod.osaas.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the &lt;code&gt;valibot&lt;/code&gt; dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; valibot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can run the development server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your web browser and go to &lt;code&gt;http://localhost:3000&lt;/code&gt; and you will see the following.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1icfmbeqevr8kqynni8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1icfmbeqevr8kqynni8m.png" alt="NextJS auth example" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the Login with OpenAuth button.&lt;/p&gt;

&lt;p&gt;Create a new account by clicking on the link Register.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57wh7u1s6n0w6u0snzud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57wh7u1s6n0w6u0snzud.png" alt="Register account" width="512" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter an email and password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2ctxp0htnkds17gj3hr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2ctxp0htnkds17gj3hr.png" alt="Entered email and password" width="528" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go your inbox in the fake SMTP mailer (or a real one if you are using a real emailer).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9qtdsrrrmfggmf0hzkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9qtdsrrrmfggmf0hzkg.png" alt="Fake emailer" width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the code you received in the email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wb5ksnl7aw5l0vk1q25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wb5ksnl7aw5l0vk1q25.png" alt="Entered code" width="602" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new user has now been created and we can verify that it is in our database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft89k0w4zy3di83effv40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft89k0w4zy3di83effv40.png" alt="User entered in database" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you are also logged in to the application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94fepnu97zz35m2zyrcj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94fepnu97zz35m2zyrcj.png" alt="Registered user" width="729" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can press Logout and verify that we can login with the password we created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55zntsss4kgv32x0l2dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55zntsss4kgv32x0l2dy.png" alt="Login" width="637" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we are back in!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykh0grnhfw7f6nh0bx5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykh0grnhfw7f6nh0bx5k.png" alt="Logged in yes" width="700" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also verify that we can handle users that forgot their password. Press link Forgot password and enter the email. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2yhrx0zp83uvfde1c6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2yhrx0zp83uvfde1c6n.png" alt="Enter email" width="667" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the inbox for the code and enter it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxcfdc3s5oh7qwd1oyho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxcfdc3s5oh7qwd1oyho.png" alt="Entered code" width="651" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have shown how simple you can implement user registration and password authentication in your application using open web services in Eyevinn Open Source Cloud. With open web services based on open source you are also not locked in with a single web service provider.&lt;/p&gt;

</description>
      <category>authentication</category>
      <category>oauth</category>
      <category>nextjs</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Creating a Ping service in Eyevinn Open Source Cloud</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Wed, 19 Feb 2025 21:28:57 +0000</pubDate>
      <link>https://forem.com/oscdev/creating-a-ping-service-in-eyevinn-open-source-cloud-jh3</link>
      <guid>https://forem.com/oscdev/creating-a-ping-service-in-eyevinn-open-source-cloud-jh3</guid>
      <description>&lt;p&gt;The open web service &lt;a href="https://app.osaas.io/dashboard/service/eyevinn-web-runner" rel="noopener noreferrer"&gt;Web Runner&lt;/a&gt; gives you the possibility to run your custom code in Eyevinn Open Source Cloud and building solutions based on a combination of the open web services available.&lt;/p&gt;

&lt;p&gt;As an example we are going to build a simple "ping" service that provides an endpoint for checking whether a site is responding or not. It will save the checks to an SQL database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faab8jg50kg0c5wyp5j5t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faab8jg50kg0c5wyp5j5t.png" alt="Solution diagram" width="660" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An account in Eyevinn Open Source Cloud. &lt;a href="https://app.osaas.io" rel="noopener noreferrer"&gt;Sign up here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2 available services in your plan. If you have no remaining services in your plan you can purchase each service individually or upgrade your plan.&lt;/li&gt;
&lt;li&gt;NodeJS installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install OSC Command Line Tool
&lt;/h2&gt;

&lt;p&gt;We will start by installing the CLI for Eyevinn Open Source Cloud.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @osaas/cli@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that it is correctly installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% osc &lt;span class="nt"&gt;-v&lt;/span&gt;
4.10.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go to the Eyevinn Open Source Cloud web console and navigate to Settings/API and copy the Personal Access Token. Store this in the environment variable &lt;code&gt;OSC_ACCESS_TOKEN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OSC_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;personal-access-token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initiate a NodeJS project
&lt;/h2&gt;

&lt;p&gt;Create a project folder and initiate a NodeJS project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% &lt;span class="nb"&gt;mkdir &lt;/span&gt;ping
% &lt;span class="nb"&gt;cd &lt;/span&gt;ping
% npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install typescript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @types/node typescript
% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; ts-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will be using &lt;code&gt;fastify&lt;/code&gt; as the API middleware framework so let us install that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; fastify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build the ping service
&lt;/h2&gt;

&lt;p&gt;Create a folder for the source code and create an &lt;code&gt;index.ts&lt;/code&gt; file in that folder.&lt;/p&gt;

&lt;p&gt;Add the following code to the &lt;code&gt;src/index.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;Reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4xx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/ping/:site&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isUp&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server listening at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following line to the package.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ts-node src/index.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add a file &lt;code&gt;tsconfig.json&lt;/code&gt; with the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"forceConsistentCasingInFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let us test what we have created by running the start script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm start
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ping@0.1.0 start
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ts-node src/index.ts

Server listening at http://127.0.0.1:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can test the endpoint with the following curl command to check whether the site &lt;code&gt;www.osaas.io&lt;/code&gt; is up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% curl http://localhost:8080/ping/www.osaas.io
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"up"&lt;/span&gt;:true&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Save checks to a database
&lt;/h2&gt;

&lt;p&gt;It is often interesting to keep an uptime history so we want to save the result from all checks in a database.&lt;/p&gt;

&lt;p&gt;We will be using a PostgreSQL database available as an open web service in Eyevinn Open Source Cloud. To be able to create the database from our application we will install the database module of the Eyevinn OSC javascript SDK and a Postgres client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; @osaas/client-db postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let us modify our &lt;code&gt;src/index.ts&lt;/code&gt; and add that we will setup a database first and a table if it does not exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@osaas/client-db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postgres&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_NAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sitemonitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_USER&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sitemonitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sitemonitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dbUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dbPassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;  
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dbUrl&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`CREATE TABLE IF NOT EXISTS checks (id BIGSERIAL PRIMARY KEY, site TEXT NOT NULL, up BOOLEAN NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW())`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;Reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4xx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/ping/:site&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isUp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`INSERT INTO checks (site, up) VALUES (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;isUp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isUp&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`INSERT INTO checks (site, up) VALUES (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, false)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;up&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server listening at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let us run it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will create a Postgres database called &lt;code&gt;site monitor&lt;/code&gt; and we can verify this by going to the web console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65eqx9mnh4jg7c1z1bfl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65eqx9mnh4jg7c1z1bfl.png" alt="PostgreSQL instance" width="672" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you wanted to give the database another name or use another username or password we can provide that using environment variables, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% &lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mynewdb npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we do a ping it will also save the check and the result from it in the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sitemonitor=# select * from checks;                                                                               id |         site         | up |         created_at         
----+----------------------+----+----------------------------
  1 | https://www.osaas.io | t  | 2025-02-18 22:06:00.415521
  2 | https://www.osaas.io | t  | 2025-02-18 22:06:16.508421
  3 | https://www.osaas.io | t  | 2025-02-19 20:52:54.02972
(3 rows)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run the code in Eyevinn Open Source Cloud
&lt;/h2&gt;

&lt;p&gt;Now we want to make this ping service we created available online. For that we will use the open web service Web Runner. The Web Runner fetches the code from a private (or public) GitHub repository so we will first create a repository for the orchestrator we built.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a GitHub repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a file called &lt;code&gt;.gitignore&lt;/code&gt; that contains the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dist/
node_modules/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a GitHub repository in your GitHub account and push the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% git add
% git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"my ping service"&lt;/span&gt;
% git remote add origin git@github.com:&amp;lt;your-git-org&amp;gt;/&amp;lt;git-repo&amp;gt;.git
% git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
% git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a GitHub personal access token
&lt;/h3&gt;

&lt;p&gt;For access to you your GitHub repository you need to &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic" rel="noopener noreferrer"&gt;create a GitHub Personal Access Token first&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address" rel="noopener noreferrer"&gt;Verify your email address&lt;/a&gt;, if it hasn't been verified yet.&lt;/li&gt;
&lt;li&gt;In the upper-right corner of any page on GitHub, click your profile photo, then click Settings.&lt;/li&gt;
&lt;li&gt;In the left sidebar, click  Developer settings.&lt;/li&gt;
&lt;li&gt;In the left sidebar, under  Personal access tokens, click Tokens (classic).&lt;/li&gt;
&lt;li&gt;Select Generate new token, then click Generate new token (classic).&lt;/li&gt;
&lt;li&gt;In the "Note" field, give your token a descriptive name.&lt;/li&gt;
&lt;li&gt;To give your token an expiration, select Expiration, then choose a default option or click Custom to enter a date.&lt;/li&gt;
&lt;li&gt;Select the scopes you'd like to grant this token. To use your token to access repositories from the command line, select repo. A token with no assigned scopes can only access public information. For more information, see &lt;a href="https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes" rel="noopener noreferrer"&gt;Scopes for OAuth apps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click Generate token and copy it to the clipboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Create Web Runner instance
&lt;/h3&gt;

&lt;p&gt;Now create a Web Runner instance with the link to the GitHub repository we created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% osc create eyevinn-web-runner myping &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;GitHubUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/&amp;lt;your-org&amp;gt;/&amp;lt;your-repo&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;GitHubToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-github-token&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;OscAccessToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-osc-token&amp;gt;"&lt;/span&gt;
Instance created:
&lt;span class="o"&gt;{&lt;/span&gt;
  name: &lt;span class="s1"&gt;'myping'&lt;/span&gt;,
  url: &lt;span class="s1"&gt;'https://&amp;lt;yourtenant&amp;gt;-myping.eyevinn-web-runner.auto.prod.osaas.io'&lt;/span&gt;,
  resources: &lt;span class="o"&gt;{&lt;/span&gt;
    license: &lt;span class="o"&gt;{&lt;/span&gt;
      url: &lt;span class="s1"&gt;'https://api-eyevinn-web-runner.auto.prod.osaas.io/license'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    app: &lt;span class="o"&gt;{&lt;/span&gt;
      url: &lt;span class="s1"&gt;'https://&amp;lt;yourtenant&amp;gt;-myping.eyevinn-web-runner.auto.prod.osaas.io/'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  GitHubUrl: &lt;span class="s1"&gt;'https://github.com/&amp;lt;your-org&amp;gt;/&amp;lt;your-repo&amp;gt;'&lt;/span&gt;,
  GitHubToken: &lt;span class="s1"&gt;'&amp;lt;your-github-token&amp;gt;'&lt;/span&gt;,
  OscAccessToken: &lt;span class="s1"&gt;'&amp;lt;your-osc-token&amp;gt;'&lt;/span&gt;,
  ConfigService: &lt;span class="s1"&gt;'undefined'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After about a minute your ping service is available at the URL &lt;code&gt;https://eyevinnlab-myping.eyevinn-web-runner.auto.prod.osaas.io&lt;/code&gt; and we can verify this with curl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% curl https://eyevinnlab-myping.eyevinn-web-runner.auto.prod.osaas.io/ping/www.osaas.io
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"up"&lt;/span&gt;:true&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can verify that it has saved it to the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sitemonitor=# select * from checks;
 id |         site         | up |         created_at         
----+----------------------+----+----------------------------
  1 | https://www.osaas.io | t  | 2025-02-18 22:06:00.415521
  2 | https://www.osaas.io | t  | 2025-02-18 22:06:16.508421
  3 | https://www.osaas.io | t  | 2025-02-19 20:52:54.02972
  4 | https://www.osaas.io | t  | 2025-02-19 21:08:51.192199
(4 rows)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provide configuration environment variables to Web Runner
&lt;/h2&gt;

&lt;p&gt;If you recall we made it possible to specify which database server it would create or use with the environment variable &lt;code&gt;DB_NAME&lt;/code&gt;. How do we provide this configuration to the ping service running as a Web Runner?&lt;/p&gt;

&lt;p&gt;For this we can use the &lt;a href="https://app.osaas.io/dashboard/service/eyevinn-app-config-svc" rel="noopener noreferrer"&gt;Application Config open web service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So let us start by creating an instance using the OSC command line tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% osc web config-create sitemonitor
Configuration service instance available at https://eyevinnlab-sitemonitor.eyevinn-app-config-svc.auto.prod.osaas.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Opening this URL in a web browser you are presented with a user interface where you can manage configuration variables. Let us add a variable for the database name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9sg0oh79glk2rkvs06i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9sg0oh79glk2rkvs06i.png" alt="Config service" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let us replace the web runner we created. Start by removing the one we created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% osc remove eyevinn-web-runner myping
Are you sure you want to remove myping? &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;/no&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;yes
&lt;/span&gt;Instance removed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when we now create it again we will refer to the application config service that we created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% osc create eyevinn-web-runner myping &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;GitHubUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://github.com/&amp;lt;your-org&amp;gt;/&amp;lt;your-repo&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;GitHubToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-github-token&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;OscAccessToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-osc-token&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;ConfigService&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sitemonitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we look at the logs we see that it has loaded the environment variables from the application config instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4tyymp1zee0ym8aa1uk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4tyymp1zee0ym8aa1uk.png" alt="Web Runner log" width="729" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let us try to ping again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% curl https://eyevinnlab-myping.eyevinn-web-runner.auto.prod.osaas.io/ping/www.osaas.io
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"up"&lt;/span&gt;:true&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And check the new database &lt;code&gt;mynewdb&lt;/code&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mynewdb=# select * from checks;
 id |         site         | up |         created_at         
----+----------------------+----+----------------------------
  1 | https://www.osaas.io | t  | 2025-02-19 21:20:43.954277
(1 row)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was a start and example on how you with the Web Runner open web service can start building solutions based on available open web service in Eyevinn Open Source Cloud without having to provision or manage your own infrastructure.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>uptime</category>
    </item>
    <item>
      <title>Publish a NodeJS application online with Eyevinn Open Source Cloud</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Sat, 15 Feb 2025 12:09:59 +0000</pubDate>
      <link>https://forem.com/oscdev/publish-a-nodejs-application-online-with-eyevinn-open-source-cloud-58c0</link>
      <guid>https://forem.com/oscdev/publish-a-nodejs-application-online-with-eyevinn-open-source-cloud-58c0</guid>
      <description>&lt;p&gt;The open web service &lt;a href="https://app.osaas.io/dashboard/service/eyevinn-web-runner" rel="noopener noreferrer"&gt;Web Runner&lt;/a&gt; takes you from localhost to https in seconds. Deploy your Node application from your public or private GitHub repository and make it instantly available. This tutorial walks you through how to get started deploying your web application with Eyevinn Open Source Cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this guide
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Get an API Access Token and setup project&lt;/li&gt;
&lt;li&gt;Create a GitHub personal access token&lt;/li&gt;
&lt;li&gt;Develop and publish a Hello World web application&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If you have not already done so, sign up for an OSC account.  &lt;a href="https://app.osaas.io" rel="noopener noreferrer"&gt;Sign up here&lt;/a&gt; for free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get an API Access Token and setup project
&lt;/h2&gt;

&lt;p&gt;In the Eyevinn Open Source Cloud web console click on "Settings" in the navigation menu on the left. Then click on the tab "API" and copy the personal access token to the clipboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foa5cvw9whe31lpnqckpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foa5cvw9whe31lpnqckpw.png" alt="Personal access token" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Store this token in your shell's environment in the environment variable &lt;code&gt;OSC_ACCESS_TOKEN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OSC_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;access-token-copied-above&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will create a web application using the NEXT.js framework in this example.&lt;/p&gt;

&lt;p&gt;Create a new Next.js project by running this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npx create-next-app@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the prompts, create-next-app will create a folder with your project name and install the required dependencies. We can try it out by running it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm run build
% npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have your web application available on &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febldbz9lqi9xycydkyce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febldbz9lqi9xycydkyce.png" alt="Hello World application" width="768" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a repository on GitHub
&lt;/h2&gt;

&lt;p&gt;Login or signup on GitHub and &lt;a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/quickstart-for-repositories" rel="noopener noreferrer"&gt;create a repository&lt;/a&gt; for your web application and give it a name, for example web-hello-world. It can be a private or a public repository. Push the code in the project folder, for example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% git remote add origin git@github.com:birme/web-hello-world.git
% git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
% git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojarevijsekg9e2p0s64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojarevijsekg9e2p0s64.png" alt="GitHub repository" width="768" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a GitHub personal access token
&lt;/h2&gt;

&lt;p&gt;For access to you your GitHub repository you need to &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic" rel="noopener noreferrer"&gt;create a GitHub Personal Access Token first&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address" rel="noopener noreferrer"&gt;Verify your email address&lt;/a&gt;, if it hasn't been verified yet.&lt;/li&gt;
&lt;li&gt;In the upper-right corner of any page on GitHub, click your profile photo, then click Settings.&lt;/li&gt;
&lt;li&gt;In the left sidebar, click  Developer settings.&lt;/li&gt;
&lt;li&gt;In the left sidebar, under  Personal access tokens, click Tokens (classic).&lt;/li&gt;
&lt;li&gt;Select Generate new token, then click Generate new token (classic).&lt;/li&gt;
&lt;li&gt;In the "Note" field, give your token a descriptive name.&lt;/li&gt;
&lt;li&gt;To give your token an expiration, select Expiration, then choose a default option or click Custom to enter a date.&lt;/li&gt;
&lt;li&gt;Select the scopes you'd like to grant this token. To use your token to access repositories from the command line, select repo. A token with no assigned scopes can only access public information. For more information, see &lt;a href="https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes" rel="noopener noreferrer"&gt;Scopes for OAuth apps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click Generate token and copy it to the clipboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Store token as a Service Secret
&lt;/h2&gt;

&lt;p&gt;Now navigate to the &lt;a href="https://app.osaas.io/dashboard/service/eyevinn-web-runner" rel="noopener noreferrer"&gt;Web Runner service&lt;/a&gt; in Eyevinn Open Source Cloud web console. Click on the tab "Service Secrets" and click on the button "New Secret". Give the secret a name and paste the GitHub token from your clipboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxuileprdgi71362aezuc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxuileprdgi71362aezuc.png" alt="Skärmavbild 2025-01-20 kl  17 13 14" width="456" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create web runner
&lt;/h2&gt;

&lt;p&gt;To make the web application available online you create a Web Runner instance.&lt;/p&gt;

&lt;p&gt;Click on the tab “My web-runners” and then on the button “Create web-runner”. Enter the GitHub URL for your web application code and enter a reference to the secret you created. Enter the URL to the GitHub repository that you created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvn8xn609gzb6nv4spd4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnvn8xn609gzb6nv4spd4.png" alt="Create web runner" width="463" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press create and you should now after a few minutes have an instance of your web application ready.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtorq1bos9qzyryxre9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtorq1bos9qzyryxre9d.png" alt="Web runner" width="768" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give it a minute or two and then click on the instance card and it will open up a new window or tab to your web application. If you see an error the web application build process is still ongoing and you just might need to wait another minute or so.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqq4qe1hybe4qq9oxcvp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqq4qe1hybe4qq9oxcvp0.png" alt="Hello World application online" width="768" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now share the URL to this instance to stakeholders or users you want feedback from. In this example the URL is &lt;a href="https://eyevinnlab-blog.eyevinn-web-runner.auto.prod.osaas.io" rel="noopener noreferrer"&gt;https://eyevinnlab-blog.eyevinn-web-runner.auto.prod.osaas.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as you see it is all running over HTTPS with a valid certificate.&lt;/p&gt;

&lt;p&gt;To un-publish this web application you simply remove the instance that is running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4odhxmidi9y08qs1m6z4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4odhxmidi9y08qs1m6z4.png" alt="Unpublish application" width="635" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use the OSC command line tool to create a web runner instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npx @osaas/cli create eyevinn-web-runner blog &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;GitHubUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/birme/web-hello-world"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;GitHubToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{{secrets.githubtoken}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  GitHub Action
&lt;/h2&gt;

&lt;p&gt;Using the &lt;a href="https://github.com/EyevinnOSC/action" rel="noopener noreferrer"&gt;GitHub action for OSC&lt;/a&gt; you can add this process to a build pipeline. For example automatically create a Web Runner instance of your application when a branch is updated for example.&lt;/p&gt;

&lt;p&gt;Create a web runner instance of your web application in integration and end-to-end tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have now given an example of how you quickly can share a prototype or a work-in-progress web application using an open web service in Eyevinn Open Source Cloud. As this is an open web service in Eyevinn Open Source Cloud you always have the option to run the same solution on your own premises as it is based on open source&lt;/p&gt;

&lt;p&gt;If you want to try this out you can sign up and launch one instance all for free.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Working with Serverless Open Source Databases in Eyevinn Open Source Cloud</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Sat, 15 Feb 2025 10:17:42 +0000</pubDate>
      <link>https://forem.com/oscdev/working-with-serverless-open-source-databases-in-eyevinn-open-source-cloud-3359</link>
      <guid>https://forem.com/oscdev/working-with-serverless-open-source-databases-in-eyevinn-open-source-cloud-3359</guid>
      <description>&lt;p&gt;&lt;a href="https://www.osaas.io" rel="noopener noreferrer"&gt;Eyevinn Open Source Cloud&lt;/a&gt; reduces the barrier to open source while contributing to a sustainable model for open source by giving back a share of the revenue to the creator.&lt;/p&gt;

&lt;p&gt;In this blog post we will walk through the different types of databases you have available at your fingertips without having to host the database servers yourself, and how easy it is to enable database support in your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this guide
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Get an API Access Token and setup project&lt;/li&gt;
&lt;li&gt;Insert document in a NoSQL document database&lt;/li&gt;
&lt;li&gt;Setup a messaging queue for your application&lt;/li&gt;
&lt;li&gt;Setup a database for real-time data warehousing&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An Eyevinn Open Source Cloud account. &lt;a href="https://app.osaas.io" rel="noopener noreferrer"&gt;Sign up here&lt;/a&gt; for free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get an API Access Token and setup project
&lt;/h2&gt;

&lt;p&gt;In the Eyevinn Open Source Cloud web console click on "Settings" in the navigation menu on the left. Then click on the tab "API" and copy the personal access token to the clipboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foa5cvw9whe31lpnqckpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foa5cvw9whe31lpnqckpw.png" alt="Personal access token" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Store this token in your shell's environment in the environment variable &lt;code&gt;OSC_ACCESS_TOKEN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OSC_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;access-token-copied-above&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup a NodeJS project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% &lt;span class="nb"&gt;mkdir &lt;/span&gt;db
% &lt;span class="nb"&gt;cd &lt;/span&gt;db
% npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the &lt;a href="https://js.docs.osaas.io" rel="noopener noreferrer"&gt;Javascript client SDK&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; @osaas/client-core @osaas/client-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Insert a document in a NoSQL database
&lt;/h2&gt;

&lt;p&gt;As a first example we will setup and connect to a NoSQL database, and then insert a document.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;nosql.js&lt;/code&gt; and then add the following code to setup a CouchDB instance in Eyevinn Open Source Cloud with the name "nosql" if it does not already exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@osaas/client-db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nano&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nano&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;couchdb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nosql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rootPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nano&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;dbList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mydb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mydb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mydb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Working with databases in Eyevinn OSC is a blast!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the database instance is created it will use the CouchDB javascript library to connect to the database, check whether a database called &lt;code&gt;mydb&lt;/code&gt; exists and create if it does not exist already.&lt;/p&gt;

&lt;p&gt;Then it will select this database and insert a JSON document. Before we can run this script we need to install the CouchDB javascript library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; nano
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can run the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% node nosql.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once completed we can verify this by navigating to the CouchDB service in Eyevinn Open Source Cloud web console and clicking on the instance card named &lt;code&gt;nosql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowb0dl5khdu95jzkp85u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowb0dl5khdu95jzkp85u.png" alt="Database i OSC" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was all you had to do to get started to store documents in a NoSQL database in your application, and the database is already deployed and available for production use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup a messaging queue for your application
&lt;/h2&gt;

&lt;p&gt;In the second example we will use Valkey in Eyevinn Open Source Cloud to setup a publish / subscribe messaging queue. Let us start with the subscriber part.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;subscriber.js&lt;/code&gt; and then add the following code to setup a Valkey instance and a client waiting for messages on a queue called &lt;code&gt;messages&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@osaas/client-db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valkey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;messagequeue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Waiting for messages...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Received message: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we can run this script we need to install a Redis client library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; ioredis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can run the subscriber.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% node subscriber.js
redis://default@172.232.131.169:10518
Waiting &lt;span class="k"&gt;for &lt;/span&gt;messages...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can move on writing an application that publish a message on this queue. Create a file called &lt;code&gt;publisher.js&lt;/code&gt; and insert this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@osaas/client-db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;valkey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;messagequeue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;receivedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Working with Eyevinn OSC is a blast!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Sent message to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;receivedCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; subscribers`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then when we run this script we get.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% node publisher.js
redis://default@172.232.131.169:10518
Sent message to 1 subscribers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And on the subscriber application side we will get.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for messages...
Received message: Working with Eyevinn OSC is a blast! from messages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup a database for real-time data warehousing
&lt;/h2&gt;

&lt;p&gt;In the final example we will setup a database that is suitable for working with large datasets. For this we will use ClickHouse that is available as an open web service.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;warehouse.js&lt;/code&gt; and insert the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupDatabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@osaas/client-db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@clickhouse/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setupDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clickhouse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warehouse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dbUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
  CREATE OR REPLACE TABLE trips
  (
    trip_id UInt32,
    vendor_id Enum8('1' = 1, '2' = 2, '3' = 3, '4' = 4, 'CMT' = 5, 'VTS' = 6, 'DDS' = 7, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14, '' = 15),
    pickup_date Date,
    pickup_datetime DateTime,
    dropoff_date Date,
    dropoff_datetime DateTime,
    store_and_fwd_flag UInt8,
    rate_code_id UInt8,
    pickup_longitude Float64,
    pickup_latitude Float64,
    dropoff_longitude Float64,
    dropoff_latitude Float64,
    passenger_count UInt8,
    trip_distance Float64,
    fare_amount Float32,
    extra Float32,
    mta_tax Float32,
    tip_amount Float32,
    tolls_amount Float32,
    ehail_fee Float32,
    improvement_surcharge Float32,
    total_amount Float32,
    payment_type Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4),
    trip_type UInt8,
    pickup FixedString(25),
    dropoff FixedString(25),
    cab_type Enum8('yellow' = 1, 'green' = 2, 'uber' = 3),
    pickup_nyct2010_gid Int8,
    pickup_ctlabel Float32,
    pickup_borocode Int8,
    pickup_ct2010 String,
    pickup_boroct2010 String,
    pickup_cdeligibil String,
    pickup_ntacode FixedString(4),
    pickup_ntaname String,
    pickup_puma UInt16,
    dropoff_nyct2010_gid UInt8,
    dropoff_ctlabel Float32,
    dropoff_borocode UInt8,
    dropoff_ct2010 String,
    dropoff_boroct2010 String,
    dropoff_cdeligibil String,
    dropoff_ntacode FixedString(4),
    dropoff_ntaname String,
    dropoff_puma UInt16
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(pickup_date)
ORDER BY pickup_datetime;
    `&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
INSERT INTO trips
SELECT * FROM s3(
    'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_{1..2}.gz',
    'TabSeparatedWithNames', "
    trip_id UInt32,
    vendor_id Enum8('1' = 1, '2' = 2, '3' = 3, '4' = 4, 'CMT' = 5, 'VTS' = 6, 'DDS' = 7, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14, '' = 15),
    pickup_date Date,
    pickup_datetime DateTime,
    dropoff_date Date,
    dropoff_datetime DateTime,
    store_and_fwd_flag UInt8,
    rate_code_id UInt8,
    pickup_longitude Float64,
    pickup_latitude Float64,
    dropoff_longitude Float64,
    dropoff_latitude Float64,
    passenger_count UInt8,
    trip_distance Float64,
    fare_amount Float32,
    extra Float32,
    mta_tax Float32,
    tip_amount Float32,
    tolls_amount Float32,
    ehail_fee Float32,
    improvement_surcharge Float32,
    total_amount Float32,
    payment_type Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4),
    trip_type UInt8,
    pickup FixedString(25),
    dropoff FixedString(25),
    cab_type Enum8('yellow' = 1, 'green' = 2, 'uber' = 3),
    pickup_nyct2010_gid Int8,
    pickup_ctlabel Float32,
    pickup_borocode Int8,
    pickup_ct2010 String,
    pickup_boroct2010 String,
    pickup_cdeligibil String,
    pickup_ntacode FixedString(4),
    pickup_ntaname String,
    pickup_puma UInt16,
    dropoff_nyct2010_gid UInt8,
    dropoff_ctlabel Float32,
    dropoff_borocode UInt8,
    dropoff_ct2010 String,
    dropoff_boroct2010 String,
    dropoff_cdeligibil String,
    dropoff_ntacode FixedString(4),
    dropoff_ntaname String,
    dropoff_puma UInt16
") SETTINGS input_format_try_infer_datetimes = 0    
`&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we run this script we will create a ClickHouse database, a table for Taxi trips and insert some sample data in that table.&lt;/p&gt;

&lt;p&gt;Install the ClickHouse javascript client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; @clickhouse/client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run our script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;% node warehouse.js
https://default:secret@eyevinnlab-warehouse.clickhouse-clickhouse.auto.prod.osaas.io/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now verify that we have inserted records in the table by going to the database web user interface available at &lt;code&gt;https://eyevinnlab-warehouse.clickhouse-clickhouse.auto.prod.osaas.io/play&lt;/code&gt; or by clicking on the database instance card in the Eyevinn Open Source Cloud web console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3p98e2xlzvh8tytsbke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3p98e2xlzvh8tytsbke.png" alt="ClickHouse instance card" width="531" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us count how many trips we have in the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbj3pa8d16rp2m8wjmuv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbj3pa8d16rp2m8wjmuv.png" alt="Trips" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We could then compute the average cost based on the number of passengers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwu7i2qo1zgkoraq7lx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwu7i2qo1zgkoraq7lx4.png" alt="Average cost" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now we have given you some examples of how quickly you can enabling database support in your application without having to worry about the deployment and infrastructure for it. Available as open web services in Eyevinn Open Source Cloud you are not locked in with a specific cloud vendor as it is based on open source.&lt;/p&gt;

</description>
      <category>database</category>
      <category>serverless</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>NoSQL Key-Value database with open web services</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Mon, 10 Feb 2025 20:31:06 +0000</pubDate>
      <link>https://forem.com/oscdev/nosql-key-value-database-with-open-web-services-3hg9</link>
      <guid>https://forem.com/oscdev/nosql-key-value-database-with-open-web-services-3hg9</guid>
      <description>&lt;p&gt;In this blog post we will show how quickly you can get started with storing data in a NoSQL Key-Value database in the cloud based on open web services in Eyevinn Open Source Cloud. Based on &lt;a href="https://app.osaas.io/dashboard/service/apache-couchdb" rel="noopener noreferrer"&gt;Apache CouchDB as a service in Eyevinn Open Source Cloud&lt;/a&gt; you don't have to manage and maintain the database server yourself and can immediately start to store and read your data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndut06ruaqz71rd18x40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndut06ruaqz71rd18x40.png" alt="Apache CouchDB in Eyevinn OSC" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In this guide
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Get an API Access Token and setup project&lt;/li&gt;
&lt;li&gt;Setup and create a database&lt;/li&gt;
&lt;li&gt;Insert and read documents&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://app.osaas.io" rel="noopener noreferrer"&gt;Eyevinn Open Source Cloud&lt;/a&gt; account.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get an API Access Token and setup project
&lt;/h2&gt;

&lt;p&gt;Navigate to &lt;a href="https://app.osaas.io/dashboard/settings/api" rel="noopener noreferrer"&gt;Settings / API&lt;/a&gt; in the Eyevinn Open Source Cloud web console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohq9s6nxjyr520nn3dek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohq9s6nxjyr520nn3dek.png" alt="Access token" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy this token and store in your shell's environment in the environment variable &lt;code&gt;OSC_ACCESS_TOKEN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% export OSC_ACCESS_TOKEN=&amp;lt;access-token-copied-above&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup a NodeJS project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% mkdir nosql
% cd nosql
% npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Javascript client SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npm install --save @osaas/client-core @osaas/client-services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A NoSQL database for a blogging platform
&lt;/h2&gt;

&lt;p&gt;As an example we will build a simple blog platform where we will store the blog posts in a NoSQL database. Instead of using tables, a NoSQL database stores data in JSON-like documents.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;blog.js&lt;/code&gt; and then add the following code that will create an CouchDB instance in Eyevinn Open Source Cloud with the name "example" if it does not already exists. Then it will create a database called "blog".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@osaas/client-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;createApacheCouchdbInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getApacheCouchdbInstance&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@osaas/client-services&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nano&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nano&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getApacheCouchdbInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createApacheCouchdbInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;AdminPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AdminPassword&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nano&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;dbList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will use the Apache CouchDB client SDK for javascript called &lt;code&gt;nano&lt;/code&gt; so we need to install that dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npm install --save nano
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add a &lt;code&gt;main()&lt;/code&gt; function to our file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can try to run this file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% node blog.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify that a database has been created we can go to the Eyevinn Open Source Cloud web console and click on the instance card for the database instance called "example".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi782moidiy7v5ntmxjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi782moidiy7v5ntmxjt.png" alt="Eyevinn OSC web console" width="423" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After logging in with the credentials that we specified when creating the instance we find the list of databases on this instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnn8oct541fmyxmgj8oq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnn8oct541fmyxmgj8oq.png" alt="Apache CouchDB in Eyevinn OSC" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now see that we have a database called "blog" that we created from our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Insert and read documents
&lt;/h2&gt;

&lt;p&gt;Now let us add a document in the database by adding the following to our main function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Getting started with NoSQL in Eyevinn OSC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Learn the basics of NoSQL in Eyevinn OSC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jonas Birmé&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the application again we can check in the web console that a new document has been inserted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlx4fw44t23xwzt2ming.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlx4fw44t23xwzt2ming.png" alt="A document has been added" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To query data from this database we can replace the insert in our main function with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;include_docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now running this application we will get.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% node blog.js
{
  _id: '448ac3db332c49841771e2068900197b',
  _rev: '1-dabd2ae20ac9fa41dfbee4d87dcc7861',
  title: 'Getting started with NoSQL in Eyevinn OSC',
  content: 'Learn the basics of NoSQL in Eyevinn OSC',
  author: 'Jonas Birmé',
  createdAt: '2025-02-10T20:14:53.582Z'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also query all blog posts written by the author "Jonas Birmé".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$eq&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jonas Birmé&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output will then be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% node blog.js
{ title: 'Getting started with NoSQL in Eyevinn OSC' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was a guiding example of how you quickly can get a NoSQL database available for your application without having to worry about database servers and hosting infrastructure. Something made possible with Apache CouchDB available as an open web service in Eyevinn Open Source Cloud. You are also not locked in with a specific cloud vendor as it is based on open source.&lt;/p&gt;

</description>
      <category>nosql</category>
      <category>opensource</category>
      <category>cloud</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Publish-subscribe messaging with open web services</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Mon, 10 Feb 2025 13:27:41 +0000</pubDate>
      <link>https://forem.com/oscdev/publish-subscribe-messaging-with-open-web-services-41ad</link>
      <guid>https://forem.com/oscdev/publish-subscribe-messaging-with-open-web-services-41ad</guid>
      <description>&lt;p&gt;In this blog post we will show an example of pub/sub messaging using open web services available in &lt;a href="https://www.osaas.io" rel="noopener noreferrer"&gt;Eyevinn Open Source Cloud&lt;/a&gt;. Publish-subscribe messaging, or pub/sub messaging, is an asynchronous communication model that is central for a scalable and distributed system of modules or services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firv78ylck5e7g9fjithh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firv78ylck5e7g9fjithh.png" alt="Pub sub messaging" width="651" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on the open web service &lt;a href="https://app.osaas.io/dashboard/service/valkey-io-valkey" rel="noopener noreferrer"&gt;Valkey in Eyevinn Open Source Cloud&lt;/a&gt; we can build an application using the pub/sub messaging model.&lt;/p&gt;

&lt;h2&gt;
  
  
  In this guide
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Get an API Access Token and setup project&lt;/li&gt;
&lt;li&gt;Develop a subscriber example application&lt;/li&gt;
&lt;li&gt;Develop a publisher example application&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href="https://app.osaas.io" rel="noopener noreferrer"&gt;Eyevinn Open Source Cloud&lt;/a&gt; account.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get an API Access Token and setup project
&lt;/h2&gt;

&lt;p&gt;Navigate to &lt;a href="https://app.osaas.io/dashboard/settings/api" rel="noopener noreferrer"&gt;Settings / API&lt;/a&gt; in the Eyevinn Open Source Cloud web console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohq9s6nxjyr520nn3dek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohq9s6nxjyr520nn3dek.png" alt="Access token" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy this token and store in your shell's environment in the environment variable &lt;code&gt;OSC_ACCESS_TOKEN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% export OSC_ACCESS_TOKEN=&amp;lt;access-token-copied-above&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup a NodeJS project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% mkdir pubsub
% cd pubsub
% npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Javascript client SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npm install --save @osaas/client-core @osaas/client-services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Subscriber application
&lt;/h2&gt;

&lt;p&gt;We will start by developing the subscriber application that will register (subscribe) to topics of interest. &lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;subscriber.js&lt;/code&gt;. Then add the following code to setup the Valkey instance that will be managing all topics and messages. Valkey is an open source, in-memory data store that is API compatible with a Redis client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getPortsForInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@osaas/client-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;createValkeyIoValkeyInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getValkeyIoValkeyInstance&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@osaas/client-services&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;valkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getValkeyIoValkeyInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;valkey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;valkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createValkeyIoValkeyInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getServiceAccessToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valkey-io-valkey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPortsForInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valkey-io-valkey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;token&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internalPort&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`redis://:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;valkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;externalIp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;externalPort&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No redis port found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The setup function will return a Redis URL to the Valkey instance.&lt;/p&gt;

&lt;p&gt;Next step is to start waiting for messages and first we need to install a Redis client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npm install --save ioredis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will add the following to the &lt;code&gt;subscriber.js&lt;/code&gt; file to subscribe on a topic called "messages".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Waiting for messages...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Received message: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now running this application we will get.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% node subscriber.js
Waiting for messages...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Publisher application
&lt;/h2&gt;

&lt;p&gt;Now open a new terminal and create a file called &lt;code&gt;publisher.js&lt;/code&gt;. Copy the setup function we created in the subscriber application and in addition add the following to this file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;publishMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redisUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;receivedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Message "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" published to channel "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;". Received by &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;receivedCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; subscribers.`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error publishing message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please a message to publish&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;publishMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the publisher application and publish a Hello world message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% node publisher.js "Hello world"
Message "Hello world" published to channel "messages". Received by 1 subscribers.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we will see that the subscriber application will output the message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for messages...
Received message: Hello world from messages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You have now successfully created a pub/sub messaging application  using the open source Valkey in-memory data store for the messaging. With Valkey as an open web service in Eyevinn Open Source Cloud you could immediately start building your pub/sub messaging application without having to host the Valkey instance yourself. You also avoided to be locked in with a specific cloud vendor as it is based on open source.&lt;/p&gt;

</description>
      <category>pubsub</category>
      <category>opensource</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Creating a web video application</title>
      <dc:creator>Jonas Birmé</dc:creator>
      <pubDate>Wed, 05 Feb 2025 20:12:57 +0000</pubDate>
      <link>https://forem.com/video/creating-a-web-video-application-56pb</link>
      <guid>https://forem.com/video/creating-a-web-video-application-56pb</guid>
      <description>&lt;p&gt;As a continuation to the blog post &lt;a href="https://dev.to/video/stream-video-with-open-web-services-17k5"&gt;Stream Video with Open Web Services&lt;/a&gt; we will in this post develop a web application for streaming the video we have created.&lt;/p&gt;

&lt;p&gt;In this guide we will use the build tool &lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; to setup a web project for a web application based on the React framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup the React Typescript project
&lt;/h2&gt;

&lt;p&gt;Run the following command to scaffold a new project based on React template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npm create vite@latest my-video-app -- --template react-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% cd my-video-app
% npm install
% npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser to &lt;a href="http://localhost:5173/" rel="noopener noreferrer"&gt;http://localhost:5173/&lt;/a&gt; and you will see the following template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87rpzmz02a4tmpckvg3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87rpzmz02a4tmpckvg3g.png" alt="Vite React Typescript example" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Video Player component
&lt;/h2&gt;

&lt;p&gt;Now it is time to add a video player component. We will use the &lt;a href="https://www.npmjs.com/package/@eyevinn/web-player" rel="noopener noreferrer"&gt;open source web player from Eyevinn&lt;/a&gt; in this example.&lt;/p&gt;

&lt;p&gt;First install the library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npm install --save @eyevinn/web-player
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will create a folder for our components&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% mkdir src/components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this folder we will create a file called &lt;code&gt;Player.tsx&lt;/code&gt; containing the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;webplayer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@eyevinn/web-player&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@eyevinn/web-player/dist/webplayer.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;autoplay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;webplayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
      &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;elRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h-full&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use the Player component
&lt;/h2&gt;

&lt;p&gt;Now we can use this Player component on our main page. Replace the contents in the file &lt;code&gt;src/App.tsx&lt;/code&gt; and replace the &lt;code&gt;src&lt;/code&gt; property with the URL to the video you created in the previous blog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Player&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/Player&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Vite&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;Eyevinn&lt;/span&gt; &lt;span class="nx"&gt;OSC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Player&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://eyevinnlab-devguide.minio-minio.auto.prod.osaas.io/devguide/VINN/52e124b8-ebe8-4dfe-9b59-8d33abb359ca/index.m3u8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when updating the browser you should see the video player with your video.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2kan98iw0uvns95jt6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs2kan98iw0uvns95jt6n.png" alt="A screenshot of your web video application" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the web application available online
&lt;/h2&gt;

&lt;p&gt;Now you have a working application running locally on your computer and it is time to make it available online. To make it available online we will be using Eyevinn Open Source Cloud to host this website as described in a &lt;a href="https://blog.osaas.io/2025/01/16/hosting-a-static-website/" rel="noopener noreferrer"&gt;blog post on Eyevinn Open Source Cloud blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First modify the vite config file &lt;code&gt;vite.config.ts&lt;/code&gt; to use a relative base url (default expects the root &lt;code&gt;/&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// https://vite.dev/config/&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can build the application by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to the web console of Eyevinn Open Source Cloud and obtain the access token available under Settings. Copy the token and store it in the environment variable &lt;code&gt;OSC_ACCESS_TOKEN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% export OSC_ACCESS_TOKEN=YOUR_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the following command to deploy the build. We name the website “video” which will be the bucket where the files are placed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;% npx @osaas/cli@latest web publish -s video dist/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your web application is available online at the address returned by the command above. In our example it is available at &lt;a href="https://eyevinnlab-video.minio-minio.auto.prod.osaas.io/video/index.html" rel="noopener noreferrer"&gt;https://eyevinnlab-video.minio-minio.auto.prod.osaas.io/video/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5k0amt4xi0rmjgzjf5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5k0amt4xi0rmjgzjf5u.png" alt="Screenshot of deployed application" width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This gives you an example of how you can build a video streaming application based on open source without having to host everything in-house. From video processing to the deployment of the web application based on open web services in Eyevinn Open Source Cloud. &lt;/p&gt;

&lt;p&gt;As everything is based on open source you always have the option to bring it in-house if you want to.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>videostreaming</category>
      <category>opensource</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
