Skip to main content

JavaScript/Node.js Implementation

This example demonstrates how to implement the ZeroinAI Chat Service API using JavaScript in a Node.js environment.

Backend Implementation (Node.js)

Setting Up the Project

First, create a new Node.js project and install the required dependencies:

mkdir zeroinai-chat-demo
cd zeroinai-chat-demo
npm init -y
npm install express axios dotenv cors

Create a .env file to store your API key:

ZEROINAI_API_KEY=your_api_key_here
ZEROINAI_API_URL=https://api.zeroinai.com/micro-apps/for-api-providers/chat-service

Creating the Server

Create a file named server.js with the following code:

const express = require('express');
const axios = require('axios');
const cors = require('cors');
require('dotenv').config();

const app = express();
const port = process.env.PORT || 3000;

// Middleware
app.use(cors());
app.use(express.json());
app.use(express.static('public'));

// API configuration
const API_URL = process.env.ZEROINAI_API_URL;
const API_KEY = process.env.ZEROINAI_API_KEY;

// Create a new chat session
app.post('/api/chats', async (req, res) => {
try {
const response = await axios.post(`${API_URL}/chats/`, {
token_limit: req.body.token_limit || 1000.0
}, {
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
}
});

res.json(response.data);
} catch (error) {
console.error('Error creating chat:', error.response?.data || error.message);
res.status(error.response?.status || 500).json({
error: error.response?.data || 'Failed to create chat session'
});
}
});

// Send a message and get AI response
app.post('/api/messages', async (req, res) => {
try {
const { chat_id, content } = req.body;

if (!chat_id || !content) {
return res.status(400).json({ error: 'chat_id and content are required' });
}

const response = await axios.post(`${API_URL}/chats/messages/`, {
chat_id,
content
}, {
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
}
});

res.json(response.data);
} catch (error) {
console.error('Error sending message:', error.response?.data || error.message);

// Handle token limit exceeded
if (error.response?.status === 402) {
return res.status(402).json({
error: 'Token limit exceeded',
message: 'The chat has reached its token limit'
});
}

res.status(error.response?.status || 500).json({
error: error.response?.data || 'Failed to send message'
});
}
});

// Get chat history
app.get('/api/chats/:chatId/messages', async (req, res) => {
try {
const { chatId } = req.params;
const { limit = 10, offset = 0 } = req.query;

const response = await axios.get(`${API_URL}/chats/${chatId}/messages/?limit=${limit}&offset=${offset}`, {
headers: {
'X-API-Key': API_KEY
}
});

res.json(response.data);
} catch (error) {
console.error('Error getting messages:', error.response?.data || error.message);
res.status(error.response?.status || 500).json({
error: error.response?.data || 'Failed to get chat messages'
});
}
});

// Add more tokens to a chat
app.put('/api/chats/:chatId/tokens', async (req, res) => {
try {
const { chatId } = req.params;
const { additional_tokens } = req.body;

if (!additional_tokens) {
return res.status(400).json({ error: 'additional_tokens is required' });
}

const response = await axios.put(`${API_URL}/chats/${chatId}/tokens/`, {
additional_tokens
}, {
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
}
});

res.json(response.data);
} catch (error) {
console.error('Error adding tokens:', error.response?.data || error.message);
res.status(error.response?.status || 500).json({
error: error.response?.data || 'Failed to add tokens'
});
}
});

// Delete a chat
app.delete('/api/chats/:chatId', async (req, res) => {
try {
const { chatId } = req.params;

const response = await axios.delete(`${API_URL}/chats/${chatId}`, {
headers: {
'X-API-Key': API_KEY
}
});

res.json(response.data);
} catch (error) {
console.error('Error deleting chat:', error.response?.data || error.message);
res.status(error.response?.status || 500).json({
error: error.response?.data || 'Failed to delete chat'
});
}
});

// Start the server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

Frontend Implementation (JavaScript)

Create a public folder in your project and add the following files:

HTML (public/index.html)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZeroinAI Chat Demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="chat-container">
<div class="chat-header">
<h1>ZeroinAI Chat Demo</h1>
</div>

<div class="chat-messages" id="chat-messages"></div>

<div class="chat-input-container">
<input type="text" id="message-input" placeholder="Type your message...">
<button id="send-button">Send</button>
</div>

<div class="token-info">
<div id="token-usage">Tokens: 0 / 1000</div>
<button id="add-tokens-button">Add 500 Tokens</button>
</div>
</div>

<script src="app.js"></script>
</body>
</html>

CSS (public/styles.css)

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

.chat-container {
width: 100%;
max-width: 600px;
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
height: 80vh;
}

.chat-header {
padding: 15px;
background-color: #007BFF;
color: white;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}

.chat-header h1 {
font-size: 20px;
font-weight: normal;
}

.chat-messages {
flex: 1;
padding: 15px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 10px;
}

.message {
padding: 10px 15px;
border-radius: 18px;
max-width: 80%;
word-wrap: break-word;
}

.user-message {
align-self: flex-end;
background-color: #007BFF;
color: white;
}

.ai-message {
align-self: flex-start;
background-color: #f0f0f0;
color: #333;
}

.system-message {
align-self: center;
background-color: #f8d7da;
color: #721c24;
font-size: 14px;
padding: 8px 12px;
border-radius: 10px;
}

.chat-input-container {
display: flex;
padding: 15px;
border-top: 1px solid #eee;
}

#message-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 20px;
outline: none;
}

#send-button {
margin-left: 10px;
padding: 10px 15px;
background-color: #007BFF;
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
}

.token-info {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background-color: #f8f9fa;
border-top: 1px solid #eee;
font-size: 14px;
}

#add-tokens-button {
padding: 5px 10px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 12px;
}

JavaScript (public/app.js)

// State management
let chatId = null;
let tokenLimit = 1000;
let tokenUsed = 0;

// DOM elements
const chatMessages = document.getElementById('chat-messages');
const messageInput = document.getElementById('message-input');
const sendButton = document.getElementById('send-button');
const tokenUsageElement = document.getElementById('token-usage');
const addTokensButton = document.getElementById('add-tokens-button');

// Initialize chat session
async function initChat() {
try {
displaySystemMessage('Creating chat session...');

const response = await fetch('/api/chats', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ token_limit: tokenLimit })
});

if (!response.ok) {
throw new Error('Failed to create chat session');
}

const data = await response.json();
chatId = data.chat_id;
tokenLimit = data.token_limit;
tokenUsed = data.token_used;

updateTokenDisplay();
displaySystemMessage('Chat session created. You can start chatting!');

// Enable input after chat is initialized
messageInput.disabled = false;
sendButton.disabled = false;
} catch (error) {
console.error('Error initializing chat:', error);
displaySystemMessage('Error: Failed to create chat session. Please refresh the page.');
}
}

// Send message and get AI response
async function sendMessage() {
const content = messageInput.value.trim();

if (!content || !chatId) return;

try {
// Display user message immediately
displayMessage(content, true);
messageInput.value = '';

// Show typing indicator
const typingIndicator = displayTypingIndicator();

// Send message to API
const response = await fetch('/api/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
chat_id: chatId,
content: content
})
});

// Remove typing indicator
chatMessages.removeChild(typingIndicator);

if (response.status === 402) {
displaySystemMessage('Token limit reached. Adding more tokens...');
await addTokens();
// Retry sending the message
return sendMessage();
}

if (!response.ok) {
throw new Error('Failed to send message');
}

const data = await response.json();

// Display AI response
displayMessage(data.content, false);

// Update token usage (fetch latest chat info)
await updateChatInfo();
} catch (error) {
console.error('Error sending message:', error);
displaySystemMessage('Error: Failed to send message. Please try again.');
}
}

// Add more tokens to the chat
async function addTokens() {
try {
const response = await fetch(`/api/chats/${chatId}/tokens`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
additional_tokens: 500
})
});

if (!response.ok) {
throw new Error('Failed to add tokens');
}

const data = await response.json();
tokenLimit = data.token_limit;
tokenUsed = data.token_used;

updateTokenDisplay();
displaySystemMessage('Added 500 more tokens to the conversation.');

return true;
} catch (error) {
console.error('Error adding tokens:', error);
displaySystemMessage('Error: Failed to add more tokens.');
return false;
}
}

// Update chat info to get latest token usage
async function updateChatInfo() {
try {
// This is a simplified approach - in a real app, you might want to
// create an endpoint to get the chat details including token usage
const response = await fetch(`/api/chats/${chatId}/messages?limit=1`);

if (!response.ok) {
throw new Error('Failed to get chat info');
}

// For this example, we're just incrementing the token usage
// In a real app, you would get the actual token usage from the API
tokenUsed += 10; // Simplified token counting

updateTokenDisplay();
} catch (error) {
console.error('Error updating chat info:', error);
}
}

// Display functions
function displayMessage(content, isUser) {
const messageElement = document.createElement('div');
messageElement.className = `message ${isUser ? 'user-message' : 'ai-message'}`;
messageElement.textContent = content;

chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
}

function displaySystemMessage(content) {
const messageElement = document.createElement('div');
messageElement.className = 'system-message';
messageElement.textContent = content;

chatMessages.appendChild(messageElement);
chatMessages.scrollTop = chatMessages.scrollHeight;
}

function displayTypingIndicator() {
const typingElement = document.createElement('div');
typingElement.className = 'message ai-message';
typingElement.textContent = 'AI is typing...';

chatMessages.appendChild(typingElement);
chatMessages.scrollTop = chatMessages.scrollHeight;

return typingElement;
}

function updateTokenDisplay() {
tokenUsageElement.textContent = `Tokens: ${tokenUsed} / ${tokenLimit}`;
}

// Event listeners
sendButton.addEventListener('click', sendMessage);

messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});

addTokensButton.addEventListener('click', addTokens);

// Initialize the app
document.addEventListener('DOMContentLoaded', () => {
// Disable input until chat is initialized
messageInput.disabled = true;
sendButton.disabled = true;

initChat();
});

Running the Application

Start the server:

node server.js

Open your browser and navigate to http://localhost:3000 to use the chat application.

Key Features of This Implementation

  1. Proxy Server: The Node.js server acts as a proxy between the frontend and the ZeroinAI API, keeping your API key secure.

  2. Token Management: The application tracks token usage and automatically adds more tokens when needed.

  3. Error Handling: Comprehensive error handling for various API error scenarios.

  4. Real-time UI Updates: The UI updates in real-time to show messages, typing indicators, and system notifications.

  5. Responsive Design: The chat interface is designed to work well on both desktop and mobile devices.

Extending the Application

You can extend this basic implementation in several ways:

  1. User Authentication: Add user authentication to associate chats with specific users.

  2. Persistent Storage: Store chat IDs in localStorage or a database to allow users to continue conversations.

  3. Chat History: Implement a feature to view and search through past conversations.

  4. Custom Styling: Customize the UI to match your brand's look and feel.

  5. Message Formatting: Add support for markdown or rich text formatting in messages.

  6. Attachments: Implement file uploads and attachments in the chat interface.